Skip to content

Warm pool logs "record could not be read: database is not open" when the daemon stops right after starting #985

Closed

Description

@V3RON

A worker that stops right after its start (here http-start-failed) logs a warm-pool read failure because state.db is already closed:

{"module":"daemon.server","message":"Daemon started", ...}            ts ...451907
{"module":"daemon.server","message":"Daemon stopping","fields":{"reason":"http-start-failed"}}  ts ...451907
{"level":"warn","module":"daemon.warm-pool","message":"the warm pool's record could not be read","fields":{"error":"Error: database is not open"}}  ts ...451909
{"module":"daemon.connection-host","message":"Released daemon socket", ...}

The warning comes from src/core/warm-pool/converger.ts:233 (#learnedKinds). A pass still runs after the database is closed, so the log shows a record failure that did not happen. Seen with the fake driver, HTTP port held, listManaged latency 3 s.

Found while triaging #981.

Written by an agent.

Activity

  1. added
    bug:newReported. An agent may triage it (rule 8), and nothing more.
    and removed
    bug:newReported. An agent may triage it (rule 8), and nothing more.
    on Oct 25, 2026
  2. self-assigned this
    on Oct 25, 2026
  3. V3RON commented on Oct 25, 2026

    @V3RON
    ContributorAuthor

    Reproduction

    Defect. The warning is real, but the issue names the wrong caller: the read comes from the cleanup reaper's run, not from a warm pool pass. That run starts on daemon.started and is still in flight when the stop closes state.db.

    src/daemon/main.test.ts › "startDaemon stop with a cleanup run in flight (#985)" › "logs no warning or error once a stop lands while the cleanup run daemon.started began is still in flight". Branch bug/985-repro.

    pnpm exec vitest run --project unit src/daemon/main.test.ts -t "985"
    
    AssertionError: expected [ { …(3) } ] to deeply equal []
    +   { "fields": { "error": "Error: database is not open" },
    +     "message": "the warm pool's record could not be read",
    +     "module": "daemon.warm-pool" }
    

    The test holds the run's diskFree read, stops the daemon, then lets the read finish.

    Root cause

    src/daemon/server.ts:639 calls reaper.dispose(), then settle and dispose close state.db (src/daemon/main.ts:725). CleanupReaper.dispose (src/core/reaper.ts:82) only unsubscribes and cancels its timer. A run already past await diskFree (reaper.ts:204) keeps going and calls targetedDevices (reaper.ts:212). That calls WarmPool.targeted() (converger.ts:299), and its #learnedKinds read fails on the closed database (converger.ts:230). warmPool.drain() waits only for passes and flights, not for targeted() callers. The warning at converger.ts:233 is where the symptom shows, not the cause.

    Simplest fix

    Add CleanupReaper.settle(), which awaits #running. In #stop, await it after reaper.dispose() and before options.settle. This follows architecture rule 12: a graceful stop waits for work already in flight.

    Alternatives rejected

    • Return early when #disposed is set after each await in the reaper: an action already running still races the teardown.
    • Skip the warning in #learnedKinds once the pool is disposed: hides the read and leaves the run acting after stop.

    Risk

    • A run that is executing a destroy now holds the stop open until that destroy ends.
    • Without the fix, a run past #view executes cleanup actions after drivers are disposed. Check that the fix covers this too.

    Written by an agent.

  4. removed their assignment
    on Oct 25, 2026
  5. V3RON commented on Oct 25, 2026

    @V3RON
    ContributorAuthor

    Decision

    Maintainer accepted the triage report's proposed fix: add CleanupReaper.settle() and await it in the stop, after reaper.dispose() and before anything closes. A stop that lands during a device destroy waits for it. The regression test should prove that no cleanup work runs after the stop (for example no driver or state.db call), not only that a log line is absent (testing rule 8).

    Written by an agent.

  6. added
    bug:readyAn agent may fix it.
    and removed on Oct 25, 2026
  7. self-assigned this
    on Oct 25, 2026
  8. V3RON commented on Oct 25, 2026

    @V3RON
    ContributorAuthor

    Delivery run

    Merged: #999 (#985)
    Parked: none
    Waiting: none
    Decided: #985 — should the regression test also prove a driver action ends before driver dispose? → yes, added (maintainer Decision + testing rule 8)

    Written by an agent.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

bug:readyAn agent may fix it.

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions