{"record":{"id":"ef1fa6c57eeaf5e4","repo":"can1357/oh-my-pi","slug":"daemon-cwd-is-not-a-directory-spec-cwd","errorCode":null,"errorMessage":"Daemon cwd is not a directory: ${spec.cwd}","messagePattern":"Daemon cwd is not a directory: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/launch/broker.ts","lineNumber":632,"sourceCode":"\t\tlet record: ManagedDaemon;\n\t\ttry {\n\t\t\tconst existing = this.#records.get(spec.name);\n\t\t\tif (existing) await this.#refreshDetached(existing);\n\t\t\tif (existing && !terminalState(existing.snapshot.state)) {\n\t\t\t\tthrow new Error(`Daemon ${spec.name} is already ${existing.snapshot.state}`);\n\t\t\t}\n\t\t\tif (existing && existing.pendingCompletions.length > 0) {\n\t\t\t\tthrow new Error(`Daemon ${spec.name} has unacknowledged completion notifications`);\n\t\t\t}\n\t\t\tif (spec.ready?.log) {\n\t\t\t\ttry {\n\t\t\t\t\tnew RegExp(spec.ready.log, \"u\");\n\t\t\t\t} catch (error) {\n\t\t\t\t\tthrow new Error(`Invalid readiness regex: ${error instanceof Error ? error.message : String(error)}`);\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst stat = await fs.stat(spec.cwd);\n\t\t\tif (!stat.isDirectory()) throw new Error(`Daemon cwd is not a directory: ${spec.cwd}`);\n\t\t\tconst dir = path.join(this.#runtimeDir, \"daemons\", spec.name);\n\t\t\tconst now = Date.now();\n\t\t\trecord = {\n\t\t\t\tspec,\n\t\t\t\tsnapshot: {\n\t\t\t\t\tname: spec.name,\n\t\t\t\t\tid: crypto.randomUUID(),\n\t\t\t\t\tstate: \"starting\",\n\t\t\t\t\tcreatedAt: now,\n\t\t\t\t\tstartedAt: now,\n\t\t\t\t\trestartCount: 0,\n\t\t\t\t\toutputBytes: 0,\n\t\t\t\t\towner,\n\t\t\t\t\tpersist: spec.persist,\n\t\t\t\t\tdetached: spec.detached,\n\t\t\t\t},\n\t\t\t\tdir,\n\t\t\t\tlog: await DaemonLog.open(dir),","sourceCodeStart":614,"sourceCodeEnd":650,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/launch/broker.ts#L614-L650","documentation":"DaemonBroker.start stats spec.cwd before spawning and requires it to exist as a directory. If the path does not exist fs.stat throws ENOENT, and if it exists but is not a directory (a file, symlink to a file, etc.) this error is thrown. The broker refuses to launch a daemon with an invalid working directory.","triggerScenarios":"Passing a DaemonSpec whose cwd points to a regular file, or whose cwd path does not exist at all (fs.stat rejects with ENOENT which propagates to the caller).","commonSituations":"Typo'd or relative path in config; the directory was deleted or renamed after config was written; running from a checkout where a build artifact directory hasn't been created yet.","solutions":["Create the directory first: `mkdir -p <cwd>` (or fs.mkdir(recursive: true)) before starting the daemon.","Fix the cwd in the spec to an absolute path to an existing directory.","Check for typos or stale config pointing at a removed/renamed directory."],"exampleFix":"// before\nawait broker.start({ name: \"worker\", cwd: \"/srv/app/workers\", ... });\n// after\nimport * as fs from \"node:fs\";\nfs.mkdirSync(\"/srv/app/workers\", { recursive: true });\nawait broker.start({ name: \"worker\", cwd: \"/srv/app/workers\", ... });","handlingStrategy":"validation","validationCode":"import { statSync } from \"node:fs\";\nconst st = statSync(spec.cwd); // throws ENOENT if missing\nif (!st.isDirectory()) throw new Error(`cwd is not a directory: ${spec.cwd}`);","typeGuard":null,"tryCatchPattern":"try {\n  await broker.start(spec);\n} catch (err) {\n  if (err instanceof Error && (err.message.startsWith(\"Daemon cwd is not a directory\") || (err as NodeJS.ErrnoException).code === \"ENOENT\")) {\n    fs.mkdirSync(spec.cwd, { recursive: true });\n    await broker.start(spec);\n  } else throw err;\n}","preventionTips":["Resolve cwd to an absolute path at config load time and verify it exists.","Create working directories as part of setup/bootstrap before launching daemons.","Never point cwd at a file path or a directory that build steps may delete."],"tags":["filesystem","configuration","path"],"backgroundTag":"path-is-not-a-directory","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}