Page workers skip most planned pages, and a second submit_plan is a run-killing fatal
Page workers skip most planned pages, and the planner can kill the run after it wrote a valid plan
Environment: [email protected], OPENWIKI_PROVIDER=openai-compatible against an OpenAI-compatible endpoint (OPENAI_COMPATIBLE_BASE_URL = https://ollama.com/v1), OPENWIKI_MODEL_ID = our own cloud model id, OPENWIKI_OPENAI_COMPATIBLE_STREAMING=true, OPENWIKI_MAX_OUTPUT_TOKENS=16384, OPENWIKI_TELEMETRY_DISABLED=1, openwiki code --update --print, run from GitHub Actions in a private repository (so the run URLs are not public; happy to paste logs into whichever issue you prefer).
1. Page workers exit without submitting, and the run reports success
Every dispatched update run skips a large share of the pages it planned, while the process still exits 0 and the diff is simply smaller:
| plan size | pages skipped | logged line |
|---|---|---|
| 11 | 7 | It was skipped for this update and will be reconsidered on the next update |
| 13 | 8 | same |
| 18 | 14 | same |
| 16 | 16 | same |
Read off dist/agent/repository-runner.js: runPageAgent (line 334) calls streamWorkerTools; a worker that finishes without a submit_page call (line 412) or that throws something which is not a fatalSubmissionFailure (line 411) ends in skipRepositoryPage + emitDeferredPageWarning (line 423), whose text is
<page> was restored after its worker exited without submitting. It was skipped for this update and will be reconsidered on the next update.submitPageTool converts only invalid_input into a rejection the model can read (dist/agent/repository-runner.js:266, :373), so a worker that ends its turn early — or hits your own turn/tool budget — is dropped instead of retried or surfaced.
The fourth row is a different model id on the same repository and the same workflow, everything else equal; it skipped every planned page, so the share is partly a property of the model.
What would help:
- a per-page skip reason in the run summary (budget exhausted vs. no submission vs. tool error), and a non-zero exit (or a documented opt-in) when a run skips pages it planned;
- a bounded in-run retry for a skipped page, the way the planning step is retried elsewhere;
- documentation of this behaviour: today "skipped for this update" only appears in the log, and a CI consumer has no way to tell a full refresh from a 30% refresh.
2. submitRepositoryPlan throws a fatal on the second call
Three runs on the same day failed before writing a single page:
Repository planning worker exited without submit_plan.(two runs), and
This OpenWiki run already has a different persisted plan.The second one is thrown by submitRepositoryPlan at dist/generation/repository-run.js:450 as RepositoryRunError("invalid_state", …). The tool boundary converts only invalid_input into a rejection the model can read, so this exception leaves the worker and the whole run exits 1 — with a valid plan on disk and zero pages generated, ~40 seconds after the heartbeat read phase=generating: 0/13 page jobs done. A model that calls submit_plan twice with a slightly different plan (an easy failure mode for a long planning turn) therefore costs the entire run instead of being told to stop.
Could that path be a tool rejection ("a plan is already installed; continue with it") rather than a fatal? It is the difference between an hour of provider tokens and a retry.
Our mitigation, for reference: we wrap the generator in a script that resets openwiki/.run.json and the generated pages to HEAD and re-runs once, only for those two failures. That is a workaround for behaviour we would rather not have to know about.
Source: langchain-ai/openwiki