stablyai/orca · error · Error
Watcher supervisor leaked its canary directory: ${watcherCan
Error message
Watcher supervisor leaked its canary directory: ${watcherCanaryDir} What it means
The harness creates a canary temp directory (watcherCanaryDir) that the supervisor's dispose() is expected to clean up. After main()'s finally block runs dispose + rm of the test roots, the script checks existsSync(watcherCanaryDir); if the dir is still present, the supervisor leaked it and the run fails. This is a resource-leak invariant enforced outside the try/finally so it runs even on prior failures.
Source
Thrown at config/scripts/runtime-file-watcher-fault-harness.mjs:176
postCrashEventDelivered: true
})
)
} finally {
try {
await subscription?.unsubscribe()
} finally {
supervisor?.dispose()
await Promise.all([
createdRootPath ? rm(createdRootPath, { recursive: true, force: true }) : Promise.resolve(),
rootPath && rootPath !== createdRootPath
? rm(rootPath, { recursive: true, force: true })
: Promise.resolve(),
bundleDir ? rm(bundleDir, { recursive: true, force: true }) : Promise.resolve()
])
}
}
if (watcherCanaryDir && existsSync(watcherCanaryDir)) {
throw new Error(`Watcher supervisor leaked its canary directory: ${watcherCanaryDir}`)
}
}
await main()
View on GitHub (pinned to 1136503c6a)
Solutions
- Ensure WatcherProcessSupervisor.dispose() removes the canary dir it created (mirror the creation path exactly).
- Verify the harness records watcherCanaryDir from the same variable the supervisor uses to create it.
- If dispose() can throw, wrap its cleanup so the canary rm always runs; the harness's own finally should also rm it as a backstop.
- Run with a clean /tmp and re-test — a stale dir from a prior crashed run can also trigger this; ensure the harness uses mkdtemp so names are unique.
Example fix
// before
// supervisor.dispose() does not remove canary dir
// after (in WatcherProcessSupervisor)
dispose() {
// ...teardown...
rmSync(this.canaryDir, { recursive: true, force: true })
} Defensive patterns
Strategy: validation
Validate before calling
const { existsSync } = require('node:fs')
if (watcherCanaryDir && existsSync(watcherCanaryDir)) {
console.error(`Watcher supervisor leaked canary dir: ${watcherCanaryDir}`)
// optional backstop: rmSync(watcherCanaryDir, { recursive: true, force: true })
process.exit(1)
} Try / catch
try { /* harness body */ }
finally {
supervisor?.dispose()
if (watcherCanaryDir && existsSync(watcherCanaryDir)) {
rmSync(watcherCanaryDir, { recursive: true, force: true }) // backstop
throw new Error(`Watcher supervisor leaked its canary directory: ${watcherCanaryDir}`)
}
} Prevention
- Make dispose() idempotently remove the canary dir it created, mirroring the creation path.
- Record watcherCanaryDir from the same variable the supervisor uses to create it.
- Add an rmSync backstop in the harness finally so a leak does not leave temp dirs on disk.
When it happens
Trigger: WatcherProcessSupervisor.dispose() did not remove the canary dir it created during setup — e.g. it tracks the dir but skips deletion, throws during cleanup, or the dir path it deletes differs from the one recorded.
Common situations: A refactor of the supervisor that dropped canary cleanup; dispose() throwing before the rm step; the canary dir path being changed (different tmp prefix) so the recorded path no longer matches what dispose deletes; dispose() never called because supervisor construction itself failed partway.
Related errors
- Watcher supervisor did not expose a live child
- Watcher supervisor did not replace the faulted child
- Benchmark exact-command cleanup failed
- Benchmark trial cleanup failed
- Electron trial and cleanup failed
AI-assisted analysis of stablyai/orca@1136503c6a (2026-08-12).
Data as JSON: /api/errors/9f3c3c146dd17878.
Report an issue: GitHub.