A package worker deadline during a file's first extraction leaves the file empty for good, and the daemon reports ready #588
Labels
No labels
code-review
correctness
dos
performance
security
severity/high
severity/low
severity/medium
tech-debt
Kind/Breaking
Kind/Bug
Kind/Documentation
Kind/Enhancement
Kind/Feature
Kind/Security
Kind/Testing
Priority
Critical
Priority
High
Priority
Low
Priority
Medium
Reviewed
Confirmed
Reviewed
Duplicate
Reviewed
Invalid
Reviewed
Won't Fix
Status
Abandoned
Status
Blocked
Status
Need More Info
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
h-dv/code-index#588
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
What happens
When a language package's worker exceeds its extraction deadline (30 s by default, and the time a cold worker needs to start counts against it) on a file the index has never held before, the file is stored as a parse error with no symbols, calls or imports. The pass still succeeds, the daemon reports
ready, and the stored row carries the file's current size and timestamps, so later passes skip it. The file stays empty until its bytes change.A file that already has facts is handled differently: a transient refusal keeps the previous facts and the file is extracted again on the next pass. Only the first extraction of a file turns a transient condition into a lasting one.
Seen from a client:
ready, andfind_callerson a function called from that file answers zero resolved callers. The only trace isparse_errors: 1and the file's error textpackage refused: host.deadline_exceeded (process).Evidence
Reproduced on the v0.41.53 candidate (
integration-1114), no product edits: the fixture oftypescript_daemon_imports::named_script_imports_follow_configuration_exports_and_daemon_restart, with the JavaScript worker suspended by exact PID during the cold index until the product's own deadline ended it.Code path (line numbers on
integration-1114):crates/indexer/src/index.rs:15706starts one 30 s deadline for the extraction; queueing and worker start consume it (crates/plugin-supervisor/src/supervise.rs:1758).crates/indexer/src/index.rs:15564classifieshost.deadline_exceeded,host.worker_unavailable,host.restart_backoffandhost.package_quarantinedas transient.crates/indexer/src/index.rs:14525: with a previous row the facts are kept and the file is left for the next pass; with no previous row the refusal becomesParseResult::Error.crates/indexer/src/writer.rs:766stores that row with the current stat and hash;crates/indexer/src/reconcile.rs:97then skips it on every later pass.crates/daemon/src/main.rs:1371marks the daemon ready after any successful pass; parse errors do not hold it back.The same path is on
origin/master(v0.41.52): the files above are unchanged by the v0.41.53 train. Inspected there, not executed.How it was found
While investigating a red test on PR #587 (
package (typescript)job: the test read[]straight afterready). A side investigation reproduced the mechanism above by suspending a worker. Correction, 2026-11-14: the CI failure itself had a different cause. The same test failed again in round 3 with the new state-printing assertion (838cc557), and the daemon log shows the TypeScript package refused at start asextractor.malformed, witnessextractor.tsx.wasm: the 30 s precompile budget on a slow runner, tracked in #589. The defect described here is real and reproduced, but it has not been observed in CI.CPU starvation alone does not reproduce it: six runs on one core shared with six busy loops passed (134 s each).
Fix status
Lane
codex-r46(local, not in PR #587): a stored transient refusal is never treated as current; the shared index entry makes one more pass when a pass had transient refusals; a persistent refusal reachesready, stays owed and is shown with "will be retried on the next pass". Replaying the reproduction against the lane: first passparse_errors=1 refs_resolved=0, second passparse_errors=0 refs_resolved=2, thenready.Who is affected
Any first index on a slow or busy machine: a cold worker start plus a first file can exceed the deadline. The result is a file that is silently absent from callers, references and impact answers until it is edited.
Fix requirements
ready.extractor.malformedfor the daemon's whole lifetime #589