{"record":{"id":"b9d1015759ec6294","repo":"paperclipai/paperclip","slug":"opencode-interruption-is-unavailable","errorCode":null,"errorMessage":"OpenCode interruption is unavailable","messagePattern":"OpenCode interruption is unavailable","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/paperclip-runner/src/cli/opencode-app-server-proxy.ts","lineNumber":375,"sourceCode":"        message: { role: \"user\", text: messageText },\n      });\n      activeTurnId = turn.turnId;\n      // OpenCode normally publishes session/turn startup over SSE, but a fast\n      // completion can make the synchronous prompt response the only place the\n      // authoritative turn id is observed. Emit the normalized notification\n      // after this request's JSON-RPC response when SSE did not already announce\n      // it. runnerd buffers notifications received while awaiting a response,\n      // so replaying an earlier SSE announcement would violate strict turn\n      // binding at the outer driver.\n      queueMicrotask(() => {\n        announceTurnStarted(session!, turn.turnId);\n      });\n      result = { turn: { id: turn.turnId, status: \"inProgress\" } };\n      break;\n    }\n    case \"turn/interrupt\":\n      if (!session?.interrupt)\n        throw new Error(\"OpenCode interruption is unavailable\");\n      await session.interrupt({\n        turnId: text(params.turnId, activeTurnId ?? \"\"),\n      });\n      result = true;\n      break;\n    case \"thread/read\":\n      if (!session) throw new Error(\"OpenCode thread is not open\");\n      result = {\n        thread: {\n          id: session.ids().driverSessionId,\n          cwd,\n          turns: activeTurnId\n            ? [{ id: activeTurnId, status: \"inProgress\" }]\n            : [],\n        },\n        transcript: await session.read?.(),\n      };\n      break;","sourceCodeStart":357,"sourceCodeEnd":393,"githubUrl":"https://github.com/paperclipai/paperclip/blob/01ad8584922b5d85292b1723cae71fa0d9b07a19/packages/paperclip-runner/src/cli/opencode-app-server-proxy.ts#L357-L393","documentation":"The proxy throws this for a 'turn/interrupt' request when no session is open or the open OpenCode session object exposes no `interrupt` capability. Interruption is optional in the driver contract, so the proxy fails closed rather than silently ignoring the request.","triggerScenarios":"Calling 'turn/interrupt' (a) before a thread is opened (session null), or (b) against an OpenCode session implementation that does not implement interrupt() (no cancel support in that transport/version).","commonSituations":"Client UI always shows a stop button even when the session kind lacks cancellation; opencode CLI version or transport without interrupt support; interrupt raced with session teardown.","solutions":["Ensure 'thread/start' or 'thread/resume' completed successfully before interrupting.","Check whether the underlying OpenCode driver session supports interrupt; disable the stop control if `session.interrupt` is absent.","Upgrade the opencode CLI/adapter to a version whose session implements cancellation.","As a fallback, let the turn complete or kill the underlying process instead of calling interrupt."],"exampleFix":"// before\nawait rpc({ method: 'turn/interrupt', params: { turnId } });\n// after\nif (capabilities.interrupt) {\n  await rpc({ method: 'turn/interrupt', params: { turnId } });\n}","handlingStrategy":"type-guard","validationCode":"const canInterrupt = session !== null && typeof session.interrupt === 'function';","typeGuard":"function supportsInterrupt(s): s is { interrupt: (p: { turnId: string }) => Promise<unknown> } {\n  return s !== null && typeof (s as any).interrupt === 'function';\n}","tryCatchPattern":"try {\n  await rpc({ method: 'turn/interrupt', params: { turnId } });\n} catch (e) {\n  if (e.message.includes('interruption is unavailable')) {\n    console.warn('turn cannot be cancelled for this session kind');\n  } else throw e;\n}","preventionTips":["Gate stop/interrupt UI controls on session capability.","Open the thread before attempting any per-turn operations.","Pin an opencode CLI/adapter version known to support cancellation."],"tags":["json-rpc","capability","session"],"backgroundTag":"operation-not-supported","analyzedSha":"01ad8584922b5d85292b1723cae71fa0d9b07a19","analyzedAt":"2026-09-10T03:14:50.855Z","contentChangedAt":"2026-09-10T03:14:50.855Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}