{"record":{"id":"1201f990e9033433","repo":"can1357/oh-my-pi","slug":"windows-batch-files-require-application-cmd-exe","errorCode":null,"errorMessage":"Windows batch files require application \"cmd.exe\" with the batch path after \"/c\"","messagePattern":"Windows batch files require application \"cmd\\.exe\" with the batch path after \"/c\"","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/launch/broker.ts","lineNumber":608,"sourceCode":"\t\t\t}\n\t\t\tcase \"shutdown\":\n\t\t\t\treturn { op: \"shutdown\" };\n\t\t}\n\t}\n\n\tasync #start(spec: DaemonSpec, owner?: string): Promise<DaemonRpcResult> {\n\t\tif (!/^[A-Za-z0-9][A-Za-z0-9._-]{0,47}$/.test(spec.name)) {\n\t\t\tthrow new Error(\"Daemon name must be 1-48 letters, numbers, dots, underscores, or hyphens\");\n\t\t}\n\t\tif (spec.detached && spec.pty) {\n\t\t\tthrow new Error(\"A detached daemon cannot allocate a PTY\");\n\t\t}\n\t\tif (\n\t\t\tspec.pty &&\n\t\t\tprocess.platform === \"win32\" &&\n\t\t\t[\".bat\", \".cmd\"].includes(path.extname(spec.application).toLowerCase())\n\t\t) {\n\t\t\tthrow new Error('Windows batch files require application \"cmd.exe\" with the batch path after \"/c\"');\n\t\t}\n\t\tif (this.#startingNames.has(spec.name)) {\n\t\t\tthrow new Error(`Daemon ${spec.name} is already starting`);\n\t\t}\n\t\tthis.#startingNames.add(spec.name);\n\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\");","sourceCodeStart":590,"sourceCodeEnd":626,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/launch/broker.ts#L590-L626","documentation":"When starting a PTY daemon on Windows, DaemonBroker refuses specs whose application is a .bat or .cmd batch file invoked directly: Windows cannot exec batch scripts as images, so they must run through cmd.exe with the batch path supplied after the /c flag. The error enforces that invocation shape.","triggerScenarios":"spec.pty is true, process.platform is 'win32', and path.extname(spec.application) is .bat or .cmd — e.g. application: 'C:\\\\scripts\\\\build.bat' or 'run.cmd' passed directly to the start RPC.","commonSituations":"Config written on macOS/Linux where direct batch paths never arise, then reused on Windows; npm .cmd shims (npm.cmd, tsc.cmd) referenced directly; installers generating batch wrappers whose path is fed as the daemon application.","solutions":["Set spec.application to 'cmd.exe' and pass the batch file as the first argument after '/c' (e.g. args: ['/c', 'C:\\\\scripts\\\\build.bat', ...])","Use the underlying executable directly (e.g. node.exe script.js) instead of its .cmd shim when possible","Guard the spec builder to apply the cmd.exe wrapping only on win32 when a PTY is requested"],"exampleFix":"// before\nawait broker.start({ name: 'build', pty: true, application: 'C:\\\\scripts\\\\build.bat' }); // throws on Windows\n// after\nawait broker.start({ name: 'build', pty: true, application: 'cmd.exe', args: ['/c', 'C:\\\\scripts\\\\build.bat'] });","handlingStrategy":"validation","validationCode":"function toPtyApplication(spec: DaemonSpec): DaemonSpec {\n  if (process.platform !== 'win32' || !spec.pty) return spec;\n  const ext = spec.application.slice(spec.application.lastIndexOf('.')).toLowerCase();\n  if (ext === '.bat' || ext === '.cmd') {\n    return { ...spec, application: 'cmd.exe', args: ['/c', spec.application, ...(spec.args ?? [])] };\n  }\n  return spec;\n}\n// pass through toPtyApplication(spec) before start","typeGuard":null,"tryCatchPattern":"try {\n  await broker.start(spec);\n} catch (err) {\n  if (err instanceof Error && err.message.includes('Windows batch files require application')) {\n    return broker.start({ ...spec, application: 'cmd.exe', args: ['/c', spec.application, ...(spec.args ?? [])] });\n  }\n  throw err;\n}","preventionTips":["On Windows, never assign .bat/.cmd paths directly as spec.application — always wrap with cmd.exe /c","Prefer the real executable (node.exe etc.) over npm's .cmd shims","Test daemon specs on Windows (or CI matrix) before shipping cross-platform configs","Use path.extname checks in spec builders to auto-wrap batch scripts"],"tags":["windows","pty","daemon","configuration"],"backgroundTag":"windows-batch-execution","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}