openclaw/openclaw · error

Codex app-server conversation binding requires a binding id

Error message

Codex app-server conversation binding requires a binding id

What it means

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.

Source

Thrown at extensions/codex/src/app-server/session-binding.ts:1316

    const rawAgentId = identity.agentId.trim();
    const sessionId = identity.sessionId.trim();
    if (!rawAgentId) {
      throw new Error("Codex app-server binding requires an agent id");
    }
    if (!sessionId) {
      throw new Error("Codex app-server binding requires a session id");
    }
    const agentId = resolveSessionAgentIds({ agentId: rawAgentId }).sessionAgentId;
    const sessionKey = identity.sessionKey?.trim();
    if (sessionKey) {
      const digest = createHash("sha256").update(sessionKey).digest("base64url");
      return `session-key:${agentId}:${digest}`;
    }
    return `session:${agentId}:${sessionId}`;
  }
  const bindingId = identity.bindingId.trim();
  if (!bindingId) {
    throw new Error("Codex app-server conversation binding requires a binding id");
  }
  return `conversation:${bindingId}`;
}

export function readStoredCodexAppServerBinding(
  value: unknown,
): StoredCodexAppServerBinding | undefined {
  const result = storedBindingSchema.safeParse(value);
  return result.success
    ? (stripUndefinedValue(result.data) as StoredCodexAppServerBinding)
    : undefined;
}

function storedSessionGeneration(
  identity: CodexAppServerBindingIdentity,
  current: StoredCodexAppServerBinding | undefined,
): { sessionId?: string } {
  if (identity.kind === "session") {

View on GitHub (pinned to 01804a7531)

Solutions

  1. Set identity.bindingId to a non-empty conversation binding identifier before any binding-store call.
  2. If you meant to use a session-scoped binding, set kind: 'session' with agentId and sessionId instead.
  3. Add a constructor-time assertion that bindingId is non-empty for conversation identities.
  4. Trace the identity's origin and ensure bindingId is populated upstream.

Example fix

// before
const identity = { kind: 'conversation', bindingId: '' };

// after
const identity = { kind: 'conversation', bindingId: conversation.bindingId };
Defensive patterns

Strategy: validation

Validate before calling

function hasBindingId(identity: CodexAppServerBindingIdentity): boolean {
  return identity.kind !== 'conversation' || (typeof identity.bindingId === 'string' && identity.bindingId.trim() !== '');
}

if (!hasBindingId(identity)) {
  // do not call the binding store; populate bindingId or switch to kind: 'session'

Type guard

export function conversationIdentityHasBindingId(i: CodexAppServerBindingIdentity): boolean {
  return i.kind !== 'conversation' || (typeof i.bindingId === 'string' && i.bindingId.trim() !== '');
}

Try / catch

try {
  const key = bindingStoreKey(identity);
} catch (error) {
  if (error instanceof Error && /conversation binding requires a binding id/.test(error.message)) {
    // caller bug: populate identity.bindingId before any binding-store call
  }
  throw error;
}

Prevention

When it happens

Trigger: Calling any binding-store operation with a conversation identity whose bindingId is blank/whitespace. bindingStoreKey runs first and rejects before state access.

Common situations: 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.

Related errors


AI-assisted analysis of openclaw/openclaw@01804a7531 (2026-08-12). Data as JSON: /api/errors/47d2ac3384f3ae75. Report an issue: GitHub.