{"record":{"id":"2537f50eed0ca50f","repo":"paperclipai/paperclip","slug":"acpx-runtime-omitted-backendsessionid","errorCode":null,"errorMessage":"ACPX runtime omitted backendSessionId","messagePattern":"ACPX runtime omitted backendSessionId","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/paperclip-runner/src/drivers/acpx/codex-runtime-adapter.ts","lineNumber":1814,"sourceCode":"      tokensUsed: optionalNumber(goal.tokensUsed),\n      timeUsedSeconds: optionalNumber(goal.timeUsedSeconds),\n      iterations: optionalNumber(goal.iterations),\n      lastReason:\n        goal.lastReason === null || typeof goal.lastReason === \"string\"\n          ? goal.lastReason\n          : undefined,\n      createdAt: optionalTimestamp(goal.createdAt),\n      updatedAt: optionalTimestamp(goal.updatedAt),\n    },\n  };\n}\n\nfunction requireIdentity(handle: AcpRuntimeHandle): AcpxRuntimePortIdentity {\n  const acpxRecordId = nonEmptyRuntimeIdentity(handle.acpxRecordId);\n  if (!acpxRecordId) throw new Error(\"ACPX runtime omitted acpxRecordId\");\n  const backendSessionId = nonEmptyRuntimeIdentity(handle.backendSessionId);\n  if (!backendSessionId) {\n    throw new Error(\"ACPX runtime omitted backendSessionId\");\n  }\n  return {\n    acpxRecordId,\n    backendSessionId,\n    // ACPX agents do not all advertise a distinct native thread identity.\n    // In that case the backend ID is the real ACP protocol session, so retain\n    // it explicitly rather than inventing a Paperclip-owned identifier.\n    agentSessionId:\n      nonEmptyRuntimeIdentity(handle.agentSessionId) ?? backendSessionId,\n  };\n}\n\nfunction definedEnvironment(\n  environment: Readonly<NodeJS.ProcessEnv>,\n): Record<string, string> {\n  return Object.fromEntries(\n    Object.entries(environment).filter(\n      (entry): entry is [string, string] => entry[1] !== undefined,","sourceCodeStart":1796,"sourceCodeEnd":1832,"githubUrl":"https://github.com/paperclipai/paperclip/blob/01ad8584922b5d85292b1723cae71fa0d9b07a19/packages/paperclip-runner/src/drivers/acpx/codex-runtime-adapter.ts#L1796-L1832","documentation":"requireIdentity() also requires a non-empty backendSessionId on the ACP runtime handle. The backendSessionId is the real ACP protocol session id used to route traffic to the backend agent; a handle without it cannot participate in the ACP session, so the adapter throws before establishing port identity.","triggerScenarios":"Calling requireIdentity() (directly or via port setup) with an AcpRuntimeHandle whose backendSessionId is undefined/null/empty — e.g. the ACPX runtime never reported a session id, session initialization failed silently, or the handle was assembled from a partial event payload.","commonSituations":"ACP initialize/session-new handshake did not complete but downstream code proceeded; runtime crash/restart produced a fresh handle without re-initializing the session; tests or scripts fabricating handles with only acpxRecordId set; agent adapter version mismatch where session id is reported under a different field name.","solutions":["Verify the ACP session initialization (session/new) completed successfully and store the returned session id into handle.backendSessionId before opening ports.","Validate the handle before use: assert handle.backendSessionId is a non-empty trimmed string.","If the runtime restarted, re-run session initialization to obtain a new backendSessionId rather than reusing the stale handle.","Check adapter/runtime version alignment if the session id arrives under a different property name."],"exampleFix":"// before\nconst handle = { acpxRecordId: recordId } as AcpRuntimeHandle;\nconst identity = requireIdentity(handle); // throws\n\n// after\nconst handle = { acpxRecordId: recordId, backendSessionId: session.id } as AcpRuntimeHandle;\nconst identity = requireIdentity(handle);","handlingStrategy":"validation","validationCode":"function hasBackendSessionId(h) {\n  return typeof h?.backendSessionId === 'string' && h.backendSessionId.trim().length > 0;\n}\nif (!hasBackendSessionId(handle)) throw new Error('handle missing backendSessionId before port setup');","typeGuard":"function hasBackendSessionId(h: AcpRuntimeHandle): h is AcpRuntimeHandle & { backendSessionId: string } {\n  return typeof h.backendSessionId === 'string' && h.backendSessionId.trim() !== '';\n}","tryCatchPattern":"try {\n  const identity = requireIdentity(handle);\n  openPorts(identity);\n} catch (err) {\n  if (err.message === 'ACPX runtime omitted backendSessionId') {\n    // session handshake did not complete; re-run session/new\n    handle.backendSessionId = await initializeAcpSession(runtime);\n  } else throw err;\n}","preventionTips":["Await the ACP session/new handshake before constructing the handle.","Never reuse handles across runtime restarts; re-initialize to get a fresh backendSessionId.","Keep adapter and runtime versions aligned so the session id field name matches."],"tags":["identity","session","validation","acpx"],"backgroundTag":"missing-required-argument","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"}