affaan-m/ECC · error · Error
Terminal process did not start correctly.
Error message
Terminal process did not start correctly.
What it means
launchDetached throws this when the value returned by spawn() is not a usable ChildProcess (missing unref), meaning Node did not produce a real detached process handle. This is an internal invariant check that the process object is malformed rather than a spawn failure.
Solutions
- Ensure the spawn implementation passed via dependencies.spawn returns a genuine child_process.ChildProcess (or stub with unref/once).
- Update the test mock to `return Object.assign(new EventEmitter(), { unref() {} })`.
- Remove any monkey-patching of child_process.spawn that alters its return value in production code.
Example fix
// before
const fakeSpawn = () => null;
launch(plan, { spawn: fakeSpawn });
// after
const fakeSpawn = () => Object.assign(new EventEmitter(), { unref() {} });
launch(plan, { spawn: fakeSpawn }); Defensive patterns
Strategy: type-guard
Validate before calling
const spawnImpl = dependencies.spawn || require('child_process').spawn;
if (typeof spawnImpl !== 'function') throw new Error('dependencies.spawn must be a function'); Type guard
function isChildProcess(c) {
return !!c && typeof c.unref === 'function' && typeof c.once === 'function';
} Try / catch
try {
launch(plan, { spawn: mySpawn });
} catch (err) {
if (err.message === 'Terminal process did not start correctly.') {
console.error('spawn dependency returned a non-ChildProcess; fix the stub/mock.');
} else throw err;
} Prevention
- Make test doubles of spawn return objects exposing unref() and once() (EventEmitter-based).
- Type injected spawn as returning child_process.ChildProcess so TS catches bad stubs.
- Avoid monkey-patching child_process.spawn in production paths.
When it happens
Trigger: A dependency-injected/spawned implementation (dependencies.spawn) returns null, undefined, or an object lacking unref — e.g. a test stub or mocked spawn — after the try/catch around the real spawn succeeded.
Common situations: Custom dependency injection where a fake spawn returns a plain object; monkey-patching child_process.spawn in tests; unusual Node runtimes/embeddings whose spawn return differs from the documented ChildProcess API.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- candidate alias collision with physical candidate id
- candidate alias integrity verification failed
- checked goal text
- checked task text
- Codex probe failed ` : ''}
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/382b1f2e1e0f1c8a.
Report an issue: GitHub.
Appendix: source
Thrown at skills/terminal-opener/scripts/open-terminal.js:271
function reportDetachedError(error) {
process.stderr.write(`Error: ${error.message}\n`);
process.exitCode = 1;
}
function launchDetached(command, args, cwd, spawnImpl, onDetachedError) {
let child;
try {
child = spawnImpl(command, args, {
cwd,
detached: true,
shell: false,
stdio: 'ignore',
});
} catch (error) {
throw new Error(`Unable to start ${command}: ${error.message}`, { cause: error });
}
if (!child || typeof child.unref !== 'function') {
throw new Error('Terminal process did not start correctly.');
}
if (typeof child.once === 'function') {
child.once('error', error => {
onDetachedError(
new Error(`Unable to start ${command}: ${error.message}`, { cause: error })
);
});
}
child.unref();
}
function launch(plan, dependencies = {}) {
const spawnSyncImpl = dependencies.spawnSync || childProcess.spawnSync;
const spawnImpl = dependencies.spawn || childProcess.spawn;
const onDetachedError = dependencies.onDetachedError || reportDetachedError;
const capability = detectTerminalCapability(plan, spawnSyncImpl);
if (!capability.available) {
throw new Error(`${capability.reason}: ${capability.action}`);View on GitHub (pinned to 8321021c54)