{"record":{"id":"7cef981c241500f4","repo":"openclaw/openclaw","slug":"codex-session-read-parameters-must-be-an-object","errorCode":null,"errorMessage":"Codex session read parameters must be an object","messagePattern":"Codex session read parameters must be an object","errorType":"validation","errorClass":"CatalogParamsError","httpStatus":null,"severity":"error","filePath":"extensions/codex/src/session-catalog.ts","lineNumber":731,"sourceCode":"            throw error;\n          }\n          throw new Error(\"Codex app-server transcript is unavailable\", { cause: error });\n        }\n      },\n    },\n    createCodexTerminalNodeHostCommand(control, configSources),\n  ];\n}\n\ntype CodexNodeSessionTranscriptParams = {\n  threadId: string;\n  cursor?: string;\n  limit: number;\n};\n\nfunction readNodeTranscriptParams(value: unknown): CodexNodeSessionTranscriptParams {\n  if (!isRecord(value)) {\n    throw new CatalogParamsError(\"Codex session read parameters must be an object\");\n  }\n  requireOnlyKeys(value, new Set([\"threadId\", \"cursor\", \"limit\"]));\n  const threadId = readBoundedOptionalString(value, \"threadId\", MAX_SESSION_ID_LENGTH);\n  if (!threadId) {\n    throw new CatalogParamsError(\"threadId is required\");\n  }\n  const cursor = readBoundedOptionalString(value, \"cursor\", MAX_CURSOR_LENGTH);\n  const limit = readBoundedLimit(\n    value.limit,\n    \"limit\",\n    DEFAULT_TRANSCRIPT_PAGE_LIMIT,\n    MAX_TRANSCRIPT_PAGE_LIMIT,\n  );\n  return { threadId, limit, ...(cursor ? { cursor } : {}) };\n}\n\nfunction readBoundedLimit(value: unknown, key: string, fallback: number, max: number): number {\n  if (value === undefined) {","sourceCodeStart":713,"sourceCodeEnd":749,"githubUrl":"https://github.com/openclaw/openclaw/blob/01804a75319da4b69c9ab98ceaa30477e22b8c0b/extensions/codex/src/session-catalog.ts#L713-L749","documentation":"Thrown by readNodeTranscriptParams when validating the parsed JSON params for the node-host transcript command. The very first check requires the params to be a plain object (isRecord); anything else (array, string, number, null, boolean) is rejected before any field is read. This is a contract guard for the node-invoke RPC shape.","triggerScenarios":"runtime.nodes.invoke is called with a params value that JSON-parses to a non-object (e.g. a bare string threadId, an array, or null) for the CODEX_APP_SERVER_THREAD_TURNS_LIST_COMMAND. parseJsonParams succeeds but isRecord returns false at line 730.","commonSituations":"Caller passes a raw threadId string instead of { threadId, limit }; caller sends an array of ids; malformed hand-constructed invoke payload; a different command's param shape was copy-pasted; integration test fixture with wrong envelope.","solutions":["Ensure the invoke params object is always { threadId: string, limit?: number, cursor?: string }.","If forwarding a threadId from another surface, wrap it: { threadId, limit: DEFAULT_TRANSCRIPT_PAGE_LIMIT }.","Add a unit test asserting the invoke payload shape before calling nodes.invoke.","Validate the envelope with a schema helper (zod or the existing requireOnlyKeys path) at the call site."],"exampleFix":"// before\nawait runtime.nodes.invoke({ nodeId, command: CODEX_APP_SERVER_THREAD_TURNS_LIST_COMMAND, params: threadId });\n// after\nawait runtime.nodes.invoke({ nodeId, command: CODEX_APP_SERVER_THREAD_TURNS_LIST_COMMAND, params: { threadId, limit: 50 } });","handlingStrategy":"validation","validationCode":"function isValidTranscriptParams(v: unknown): v is { threadId: string; limit?: number; cursor?: string } {\n  return typeof v === 'object' && v !== null && !Array.isArray(v);\n}\nif (!isValidTranscriptParams(params)) throw new Error('params must be an object');","typeGuard":"function isTranscriptParams(v: unknown): v is Record<string, unknown> {\n  return typeof v === 'object' && v !== null && !Array.isArray(v);\n}","tryCatchPattern":null,"preventionTips":["Always pass an object envelope { threadId, limit?, cursor? } to nodes.invoke for this command.","Add a unit test asserting the invoke payload is a plain object.","Use a schema helper (zod) at the call site for integration code."],"tags":["codex","validation","node-host","params","transcript"],"backgroundTag":null,"analyzedSha":"01804a75319da4b69c9ab98ceaa30477e22b8c0b","analyzedAt":"2026-08-12T04:37:58.197Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}