Skip to content

think --model silently degrades to 'no LLM available' and SAVES an empty synthesis instead of erroring #1698

@garrytan

Description

@garrytan

Symptom

Running a batch of gbrain think --anchor <slug> --take --save --model "anthropic/claude-sonnet-4-6" (slash form) on v0.41.38.0, the first pass silently produced empty synthesis pages. No model ran, no error was thrown, and --save still persisted a near-empty synthesis page + take row. The failure only became visible because the output was obviously empty on inspection — the command exited 0.

Re-running the identical batch with the colon form --model "anthropic:claude-sonnet-4-6" worked perfectly: 95/95 people + 104/104 company pages enriched with real, cited synthesis.

Why this is surprising

v0.41.21.0's parseModelId (src/core/ai/model-resolver.ts) explicitly added slash-form support, with a comment that this 'Closes the end-to-end bug class.' Yet on v0.41.38.0 the slash form still failed for think — either:

  • think's model-resolution path doesn't route through the fixed parseModelId, or
  • it catches the resolution failure and falls back to a no-LLM/title-only path instead of surfacing it.

Either way the fail-silent + save-anyway behavior is the real bug, independent of the slash/colon parsing question. A model override that can't be resolved should be a hard error, never a silent degrade that still writes persisted pages.

Proposed fixes

  1. Fail loud on unresolvable --model. If the operator explicitly passed --model X and X can't be resolved to a working recipe/provider, throw AIConfigError and exit non-zero. Never fall back to no-LLM when the model was explicitly requested. (Implicit/default model fallback is fine; explicit override is a contract.)
  2. Never --save an empty synthesis. Guard persistSynthesis: if the synthesis body is empty / the LLM didn't actually run (Pages: N | Citations: 0 AND zero generated prose), refuse to persist and warn. Writing empty synthesis pages pollutes the brain and is worse than no page.
  3. Normalize slash→colon at every CLI entry point so --model anthropic/claude-sonnet-4-6 and --model anthropic:claude-sonnet-4-6 are interchangeable everywhere, matching the documented --model <alias or full id> help text. Confirm think specifically routes through parseModelId post-v0.41.21.0.
  4. Surface the resolved model in output even on success (Model: anthropic:claude-sonnet-4-6 (resolved from 'anthropic/claude-sonnet-4-6')) so silent degrades are caught immediately in logs.

Impact

In a 200-page batch this silently burned a full pass and wrote 200 empty pages before detection. For any unattended/cron enrichment job this is a data-quality landmine: the job reports success, exits 0, and quietly degrades the brain.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions