can1357/oh-my-pi · error
Windows batch files require application "cmd.exe" with the b
Error message
Windows batch files require application "cmd.exe" with the batch path after "/c"
What it means
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.
Source
Thrown at packages/coding-agent/src/launch/broker.ts:608
}
case "shutdown":
return { op: "shutdown" };
}
}
async #start(spec: DaemonSpec, owner?: string): Promise<DaemonRpcResult> {
if (!/^[A-Za-z0-9][A-Za-z0-9._-]{0,47}$/.test(spec.name)) {
throw new Error("Daemon name must be 1-48 letters, numbers, dots, underscores, or hyphens");
}
if (spec.detached && spec.pty) {
throw new Error("A detached daemon cannot allocate a PTY");
}
if (
spec.pty &&
process.platform === "win32" &&
[".bat", ".cmd"].includes(path.extname(spec.application).toLowerCase())
) {
throw new Error('Windows batch files require application "cmd.exe" with the batch path after "/c"');
}
if (this.#startingNames.has(spec.name)) {
throw new Error(`Daemon ${spec.name} is already starting`);
}
this.#startingNames.add(spec.name);
let record: ManagedDaemon;
try {
const existing = this.#records.get(spec.name);
if (existing) await this.#refreshDetached(existing);
if (existing && !terminalState(existing.snapshot.state)) {
throw new Error(`Daemon ${spec.name} is already ${existing.snapshot.state}`);
}
if (existing && existing.pendingCompletions.length > 0) {
throw new Error(`Daemon ${spec.name} has unacknowledged completion notifications`);
}
if (spec.ready?.log) {
try {
new RegExp(spec.ready.log, "u");View on GitHub (pinned to 9690622007)
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
Example fix
// before
await broker.start({ name: 'build', pty: true, application: 'C:\\scripts\\build.bat' }); // throws on Windows
// after
await broker.start({ name: 'build', pty: true, application: 'cmd.exe', args: ['/c', 'C:\\scripts\\build.bat'] }); Defensive patterns
Strategy: validation
Validate before calling
function toPtyApplication(spec: DaemonSpec): DaemonSpec {
if (process.platform !== 'win32' || !spec.pty) return spec;
const ext = spec.application.slice(spec.application.lastIndexOf('.')).toLowerCase();
if (ext === '.bat' || ext === '.cmd') {
return { ...spec, application: 'cmd.exe', args: ['/c', spec.application, ...(spec.args ?? [])] };
}
return spec;
}
// pass through toPtyApplication(spec) before start Try / catch
try {
await broker.start(spec);
} catch (err) {
if (err instanceof Error && err.message.includes('Windows batch files require application')) {
return broker.start({ ...spec, application: 'cmd.exe', args: ['/c', spec.application, ...(spec.args ?? [])] });
}
throw err;
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- A detached daemon cannot allocate a PTY
- Daemon ${operation.name} stdin is unavailable
- Daemon broker environment is incomplete
- can't determine symlink type, since it is dangling
- GetFinalPathNameByHandleW failed with code {0}
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/1201f990e9033433.
Report an issue: GitHub.