microskills: a proposer reply with `"extractors": null` (or another wrongly typed field) aborts the fold, and the next session start pays for the proposer again

Open
#173 0 comments 0 reactions 0 assignees View on GitHub

Maintainers usually reply within 1 day

@TejasLamba2006 is already working on this.

Since Nov 10, 2026.

  • #198 by @TejasLamba2006 — open

Assessment

Difficulty
3/5
Estimated time
1-2 days
Newbie friendliness
72/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Active
Tech stack
python
Domain
ai, backend

Research direction

Start with parse_payload and _rx in tokeneconomizer/microskills/distiller.py, then read the existing coverage in tests/test_generic_microskills.py. Reproduce the malformed proposer replies described in the issue and check how Distiller.update and fold_locked handle them. Done means wrongly typed entries are safely ignored, invalid patterns do not abort folding, and tests cover these cases.

Written by the indexing model from the issue text.

Description

What happens

parse_payload (tokeneconomizer/microskills/distiller.py) checks that the proposer's reply is JSON, but not the types inside it:

  • {"extractors": null, "facts": []} raises TypeError: 'NoneType' object is not iterable.
  • An extractor whose pattern is a list raises TypeError: unhashable type: 'list' in _rx, which only catches re.error.
  • A fact whose detector is a string raises AttributeError in _ground.

The guard added for #131 only covers the runner call. These errors come later, out of Distiller.update, so they abort fold_locked before folded.json or the ledger is written. The session stays pending, so the next session start folds it again and calls the proposer again.

Repro

Offline: six sessions through hook.main, with TE_MICROSKILLS=on, the default k = 3, and the proposer stubbed to answer {"extractors": null, "facts": []}.

paid proposer calls: 9        (2 expected)
folded.json: ['s0']
ledger records: 1
errors.log tail: TypeError: 'NoneType' object is not iterable

Why it matters

  • A model that writes null for "no extractors" makes every later session start call the proposer again. Each of those calls is billed, and none of them reach the ledger.
  • The envfacts layer, which makes no model calls, stops learning too.

A side note, not part of the fix

PROPOSER_SYSTEM asks for "ONLY a JSON list of <=4 such objects", and a few lines later for "a JSON OBJECT {extractors, facts}". A reply that follows the first instruction is dropped without any notice. The prompt is pinned by a hash in tests/test_generic_microskills.py, so I've left it alone.

I have a fix with tests ready: parse_payload keeps only entries with the expected types, and _rx returns None for a non-string pattern. Happy to open a PR if that approach works for you.

Dominant language
Python
Stars
5.6k
Forks
344
Avg merge
17h 41m
Merged PRs (30d)
73

Getting set up

This project ships no dev container, Dockerfile or contributing guide, so setting up is up to you: start from its README, and see our first-contribution guide for the general steps.

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from microsoft/TokenEconomizer

All issues in microsoft/TokenEconomizer

Similar issues

More Python issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.