{"record":{"id":"edbc85ec0f70fa95","repo":"can1357/oh-my-pi","slug":"bridge-call-json-stringify-name-aborted-eval","errorCode":null,"errorMessage":"bridge call ${JSON.stringify(name)} aborted: eval cell was interrupted","messagePattern":"bridge call (.+?) aborted: eval cell was interrupted","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"packages/coding-agent/src/eval/py/tool-bridge.ts","lineNumber":78,"sourceCode":" * Python invokes this bridge with blocking `urllib` requests from worker threads\n * (each `agent()` / `tool.*` call). Two different aborts meet here:\n *\n * - {@link PyToolBridgeEntry.signal} goes to the tool, so a turn cancel tears\n *   down delegated work — subagents included — instead of leaving it running\n *   past the cell.\n * - {@link PyToolBridgeEntry.shieldedSignal} decides when we may stop waiting.\n *   It is deferred across a critical `agent()` phase, so a cancel landing\n *   mid-merge cannot return early and let the cell settle while an\n *   abort-insensitive cherry-pick is still rewriting the repo.\n *\n * Calls arriving after an abort are rejected before starting. Otherwise the\n * usual path is that the tool observes its own abort and rejects; the race only\n * matters for tools that ignore the signal, keeping the kernel unwinding\n * promptly instead of being hard-killed.\n */\nasync function callSessionToolPromptOnAbort(name: string, args: unknown, entry: PyToolBridgeEntry): Promise<unknown> {\n\tif (entry.abortRequested?.()) {\n\t\tthrow new Error(`bridge call ${JSON.stringify(name)} aborted: eval cell was interrupted`);\n\t}\n\tconst call = callSessionTool(name, args, {\n\t\tsession: entry.toolSession,\n\t\tsignal: entry.signal,\n\t\temitStatus: entry.emitStatus,\n\t});\n\tconst signal = entry.shieldedSignal ?? entry.signal;\n\tif (!signal) return await call;\n\tif (signal.aborted) {\n\t\tvoid call.catch(() => {});\n\t\tthrow new Error(`bridge call ${JSON.stringify(name)} aborted: eval cell was interrupted`);\n\t}\n\tconst { promise: aborted, reject } = Promise.withResolvers<never>();\n\tconst onAbort = () => reject(new Error(`bridge call ${JSON.stringify(name)} aborted: eval cell was interrupted`));\n\tsignal.addEventListener(\"abort\", onAbort, { once: true });\n\ttry {\n\t\treturn await Promise.race([call, aborted]);\n\t} finally {","sourceCodeStart":60,"sourceCodeEnd":96,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/eval/py/tool-bridge.ts#L60-L96","documentation":"The Python eval tool bridge runs agent session tools on behalf of code executing inside a Python kernel cell. `callSessionToolPromptOnAbort` first checks the entry's `abortRequested()` probe; if the eval cell has already been interrupted, it refuses to start a new session-tool call and throws this error immediately. This is a deliberate guard so a cancelled cell cannot kick off fresh side-effecting tool work while the kernel is unwinding.","triggerScenarios":"Python eval cell code calls a bridged session tool (via callSessionToolPromptOnAbort) after the cell's abort signal has already fired — i.e. the cell was interrupted and the tool callback still runs before noticing the cancellation.","commonSituations":"A user hits interrupt/escape while a cell is mid-run; a tool-call timeout cancels the cell but the bridge callback is invoked anyway; asynchronous Python code that captured the bridge continues after the interrupt.","solutions":["Check the abort/interrupt state in your eval code before invoking the bridged tool and bail out early.","If the interruption was unintentional, re-run the eval cell without interrupting it.","If a tool must run to completion regardless of cell abort, invoke it outside the aborted cell context (e.g. in a fresh cell or session-level call)."],"exampleFix":"// before: tool callback ignores cancellation state\nconst result = await callSessionToolPromptOnAbort(name, args, entry);\n\n// after: check abort before starting work (the library now does this for you)\nif (entry.abortRequested?.()) {\n  throw new Error(`bridge call ${JSON.stringify(name)} aborted: eval cell was interrupted`);\n}","handlingStrategy":"try-catch","validationCode":"if (entry.abortRequested?.()) {\n  // skip the tool call entirely — cell is being interrupted\n  return;\n}","typeGuard":"function isAbortError(err: unknown): boolean {\n  return err instanceof Error && err.message.includes(\"aborted: eval cell was interrupted\");\n}","tryCatchPattern":"try {\n  result = await callSessionToolPromptOnAbort(name, args, entry);\n} catch (err) {\n  if (err instanceof Error && err.message.includes(\"aborted: eval cell was interrupted\")) {\n    return; // expected during cell interrupt — stop gracefully\n  }\n  throw err;\n}","preventionTips":["Check abortRequested() before each bridged tool call in eval code.","Keep tool calls short so interrupts land between calls.","Treat this error as a normal interrupt signal, not a bug."],"tags":["abort","tool-bridge","python-eval","cancellation"],"backgroundTag":"operation-aborted-signal","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}