{"record":{"id":"47d2ac3384f3ae75","repo":"openclaw/openclaw","slug":"codex-app-server-conversation-binding-requires-a-b","errorCode":null,"errorMessage":"Codex app-server conversation binding requires a binding id","messagePattern":"Codex app-server conversation binding requires a binding id","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"extensions/codex/src/app-server/session-binding.ts","lineNumber":1316,"sourceCode":"    const rawAgentId = identity.agentId.trim();\n    const sessionId = identity.sessionId.trim();\n    if (!rawAgentId) {\n      throw new Error(\"Codex app-server binding requires an agent id\");\n    }\n    if (!sessionId) {\n      throw new Error(\"Codex app-server binding requires a session id\");\n    }\n    const agentId = resolveSessionAgentIds({ agentId: rawAgentId }).sessionAgentId;\n    const sessionKey = identity.sessionKey?.trim();\n    if (sessionKey) {\n      const digest = createHash(\"sha256\").update(sessionKey).digest(\"base64url\");\n      return `session-key:${agentId}:${digest}`;\n    }\n    return `session:${agentId}:${sessionId}`;\n  }\n  const bindingId = identity.bindingId.trim();\n  if (!bindingId) {\n    throw new Error(\"Codex app-server conversation binding requires a binding id\");\n  }\n  return `conversation:${bindingId}`;\n}\n\nexport function readStoredCodexAppServerBinding(\n  value: unknown,\n): StoredCodexAppServerBinding | undefined {\n  const result = storedBindingSchema.safeParse(value);\n  return result.success\n    ? (stripUndefinedValue(result.data) as StoredCodexAppServerBinding)\n    : undefined;\n}\n\nfunction storedSessionGeneration(\n  identity: CodexAppServerBindingIdentity,\n  current: StoredCodexAppServerBinding | undefined,\n): { sessionId?: string } {\n  if (identity.kind === \"session\") {","sourceCodeStart":1298,"sourceCodeEnd":1334,"githubUrl":"https://github.com/openclaw/openclaw/blob/01804a75319da4b69c9ab98ceaa30477e22b8c0b/extensions/codex/src/app-server/session-binding.ts#L1298-L1334","documentation":"Thrown by bindingStoreKey when identity.kind !== 'session' (i.e. a conversation identity) but identity.bindingId (trimmed) is empty. Conversation-scoped bindings are keyed by bindingId; without it the store key 'conversation:<id>' cannot be formed.","triggerScenarios":"Calling any binding-store operation with a conversation identity whose bindingId is blank/whitespace. bindingStoreKey runs first and rejects before state access.","commonSituations":"A caller constructing a conversation identity without a bindingId; a refactoring that dropped the bindingId field; using the conversation-kind path without a durable conversation/binding reference.","solutions":["Set identity.bindingId to a non-empty conversation binding identifier before any binding-store call.","If you meant to use a session-scoped binding, set kind: 'session' with agentId and sessionId instead.","Add a constructor-time assertion that bindingId is non-empty for conversation identities.","Trace the identity's origin and ensure bindingId is populated upstream."],"exampleFix":"// before\nconst identity = { kind: 'conversation', bindingId: '' };\n\n// after\nconst identity = { kind: 'conversation', bindingId: conversation.bindingId };","handlingStrategy":"validation","validationCode":"function hasBindingId(identity: CodexAppServerBindingIdentity): boolean {\n  return identity.kind !== 'conversation' || (typeof identity.bindingId === 'string' && identity.bindingId.trim() !== '');\n}\n\nif (!hasBindingId(identity)) {\n  // do not call the binding store; populate bindingId or switch to kind: 'session'","typeGuard":"export function conversationIdentityHasBindingId(i: CodexAppServerBindingIdentity): boolean {\n  return i.kind !== 'conversation' || (typeof i.bindingId === 'string' && i.bindingId.trim() !== '');\n}","tryCatchPattern":"try {\n  const key = bindingStoreKey(identity);\n} catch (error) {\n  if (error instanceof Error && /conversation binding requires a binding id/.test(error.message)) {\n    // caller bug: populate identity.bindingId before any binding-store call\n  }\n  throw error;\n}","preventionTips":["Populate bindingId when constructing a conversation identity.","If you meant session-scoped, use kind: 'session' with agentId + sessionId.","Add a constructor-time assertion for non-empty bindingId on conversation identities.","Trace identity construction to ensure bindingId is sourced from a durable conversation reference."],"tags":["codex","session-binding","identity","binding-id","conversation","caller-bug"],"backgroundTag":null,"analyzedSha":"01804a75319da4b69c9ab98ceaa30477e22b8c0b","analyzedAt":"2026-08-12T04:37:58.197Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}