{"record":{"id":"edf658dc9ce3eb62","repo":"google/zx","slug":"too-late-to-abort-the-process","errorCode":null,"errorMessage":"Too late to abort the process.","messagePattern":"Too late to abort the process\\.","errorType":"exception","errorClass":"Fail","httpStatus":null,"severity":"error","filePath":"src/core.ts","lineNumber":437,"sourceCode":"    if (this.isSettled()) return\n    this._output = output\n    ProcessPromise.bus.unpipeBack(this)\n    if (output.ok || this.isNothrow()) {\n      this._stage = 'fulfilled'\n      this._resolve(output)\n    } else {\n      this._stage = 'rejected'\n      if (legacy) {\n        this._resolve(output) // to avoid unhandledRejection alerts\n        throw output.cause || output\n      }\n      this._reject(output)\n      if (this.sync) throw output\n    }\n  }\n\n  abort(reason?: string) {\n    if (this.isSettled()) throw new Fail('Too late to abort the process.')\n    if (this.signal !== this.ac.signal)\n      throw new Fail('The signal is controlled by another process.')\n    if (!this.child)\n      throw new Fail('Trying to abort a process without creating one.')\n\n    this.ac.abort(reason)\n  }\n\n  kill(signal?: NodeJS.Signals | null): Promise<void> {\n    if (this.isSettled()) throw new Fail('Too late to kill the process.')\n    if (!this.child)\n      throw new Fail('Trying to kill a process without creating one.')\n    if (!this.pid) throw new Fail('The process pid is undefined.')\n\n    return $.kill(this.pid, signal || this._snapshot.killSignal || $.killSignal)\n  }\n\n  // Configurators","sourceCodeStart":419,"sourceCodeEnd":455,"githubUrl":"https://github.com/google/zx/blob/00a2c484e219c2e84bfc3a199febf7fbce2cfbf4/src/core.ts#L419-L455","documentation":"Thrown by ProcessPromise.abort() when the process has already settled (fulfilled or rejected). Once a process exits, there is no live child to abort, so zx refuses rather than silently no-op. The guard checks isSettled() (output already assigned).","triggerScenarios":"Calling `pp.abort()` inside `.then()`, `.catch()`, or `.finally()`; after `await pp`; in a setTimeout/setInterval callback that fires after the process completed; abort logic racing with a fast command.","commonSituations":"Timeout/abort watchdogs that lose the race against quick commands; cleanup handlers running in finally blocks; abort wired to an event that fires post-completion.","solutions":["Guard the call: `if (!pp.isSettled()) pp.abort()`.","Move abort into the running window (before the awaited expression).","Use $.timeout / pp.timeout() for time-bounded kills instead of manual abort."],"exampleFix":"// before\nawait pp\npp.abort()\n// after\nif (!pp.isSettled()) pp.abort()","handlingStrategy":"validation","validationCode":"// pp.isSettled() is private; track settlement externally\nlet settled = false\npp.then(() => { settled = true }).catch(() => { settled = true })\n\nfunction safeAbort(p: ProcessPromise): void {\n  if (!settled) p.abort()\n}","typeGuard":null,"tryCatchPattern":"try {\n  pp.abort()\n} catch (e) {\n  if (e instanceof Fail && /Too late to abort/.test(e.message)) {\n    // already finished — nothing to do\n  } else throw e\n}","preventionTips":["Guard every abort() with an isSettled() / stage check (or track settlement yourself).","Prefer $.timeout for bounded execution over manual abort watchdogs.","Wire abort to the 'start' event rather than to completion handlers."],"tags":["abort","lifecycle","abortcontroller","promise"],"backgroundTag":null,"analyzedSha":"00a2c484e219c2e84bfc3a199febf7fbce2cfbf4","analyzedAt":"2026-08-13T02:11:06.305Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}