{"record":{"id":"382b1f2e1e0f1c8a","repo":"affaan-m/ECC","slug":"terminal-process-did-not-start-correctly","errorCode":null,"errorMessage":"Terminal process did not start correctly.","messagePattern":"Terminal process did not start correctly\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"skills/terminal-opener/scripts/open-terminal.js","lineNumber":271,"sourceCode":"function reportDetachedError(error) {\n  process.stderr.write(`Error: ${error.message}\\n`);\n  process.exitCode = 1;\n}\n\nfunction launchDetached(command, args, cwd, spawnImpl, onDetachedError) {\n  let child;\n  try {\n    child = spawnImpl(command, args, {\n      cwd,\n      detached: true,\n      shell: false,\n      stdio: 'ignore',\n    });\n  } catch (error) {\n    throw new Error(`Unable to start ${command}: ${error.message}`, { cause: error });\n  }\n  if (!child || typeof child.unref !== 'function') {\n    throw new Error('Terminal process did not start correctly.');\n  }\n  if (typeof child.once === 'function') {\n    child.once('error', error => {\n      onDetachedError(\n        new Error(`Unable to start ${command}: ${error.message}`, { cause: error })\n      );\n    });\n  }\n  child.unref();\n}\n\nfunction launch(plan, dependencies = {}) {\n  const spawnSyncImpl = dependencies.spawnSync || childProcess.spawnSync;\n  const spawnImpl = dependencies.spawn || childProcess.spawn;\n  const onDetachedError = dependencies.onDetachedError || reportDetachedError;\n  const capability = detectTerminalCapability(plan, spawnSyncImpl);\n  if (!capability.available) {\n    throw new Error(`${capability.reason}: ${capability.action}`);","sourceCodeStart":253,"sourceCodeEnd":289,"githubUrl":"https://github.com/affaan-m/ECC/blob/01e15490f04e29cfefe3896951f43db46994d8ee/skills/terminal-opener/scripts/open-terminal.js#L253-L289","documentation":"After spawn returns, launchDetached verifies the returned child object has an unref function (needed to detach the process from the parent's event loop). If spawnImpl returned null/undefined or a non-Child object, this throws. It is primarily a guard against a non-conformant spawn in the dependency-injection seam rather than a normal runtime condition.","triggerScenarios":"spawnImpl returns null/undefined or an object lacking unref (e.g., a test double, mock, or a custom spawn shim with the wrong return shape).","commonSituations":"Unit tests passing a fake spawn that returns {} or undefined; a custom launch() dependency injection with a broken spawnImpl; an extremely old/abnormal Node environment.","solutions":["Use the default child_process.spawn, or ensure an injected spawnImpl returns a real ChildProcess with unref().","In tests, have the spawn double return an object exposing unref() and once()."],"exampleFix":"// before (test double returns nothing)\nconst fakeSpawn = () => undefined;\nlaunch(plan, { spawn: fakeSpawn });\n\n// after\nconst fakeSpawn = () => ({ unref() {}, once() {} });\nlaunch(plan, { spawn: fakeSpawn });","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"function isChildProcess(value) {\n  return !!value && typeof value.unref === 'function' && typeof value.once === 'function';\n}\n// when injecting a spawn double into launch():\nconst spawnImpl = (cmd, args, opts) => {\n  const child = realSpawn(cmd, args, opts);\n  if (!isChildProcess(child)) throw new Error('spawn did not return a ChildProcess');\n  return child;\n};","tryCatchPattern":null,"preventionTips":["When injecting a custom spawn into launch(), return a real ChildProcess (with unref and once).","In tests, model the spawn double after child_process.spawn's return shape, including unref().","Do not substitute a generic stub for spawn; launchDetached depends on the ChildProcess contract."],"tags":["subprocess","terminal","spawn","invariant"],"backgroundTag":null,"analyzedSha":"01e15490f04e29cfefe3896951f43db46994d8ee","analyzedAt":"2026-08-13T00:31:08.655Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}