{"record":{"id":"c98a59ba28bc6373","repo":"Yeachan-Heo/oh-my-codex","slug":"session-id-must-be-a-string","errorCode":null,"errorMessage":"session_id must be a string","messagePattern":"session_id must be a string","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/mcp/state-paths.ts","lineNumber":121,"sourceCode":"\nexport type StateFileScope = 'root' | 'session';\n\nexport interface ModeStateFileRef {\n  mode: string;\n  path: string;\n  scope: StateFileScope;\n}\n\nexport function normalizeSessionId(sessionId: unknown): string | undefined {\n  if (typeof sessionId !== 'string') return undefined;\n  const normalized = sessionId.trim();\n  return SESSION_ID_PATTERN.test(normalized) ? normalized : undefined;\n}\n\nexport function validateSessionId(sessionId: unknown): string | undefined {\n  if (sessionId == null) return undefined;\n  if (typeof sessionId !== 'string') {\n    throw new Error('session_id must be a string');\n  }\n  if (!SESSION_ID_PATTERN.test(sessionId)) {\n    throw new Error('session_id must match ^[A-Za-z0-9_-]{1,64}$');\n  }\n  return sessionId;\n}\n\n\nexport function validateStateModeSegment(mode: unknown): string {\n  if (typeof mode !== 'string') {\n    throw new Error('mode must be a string');\n  }\n  const normalized = mode.trim();\n  if (!normalized) {\n    throw new Error('mode must be a non-empty string');\n  }\n  if (normalized.includes('..')) {\n    throw new Error('mode must not contain \"..\"');","sourceCodeStart":103,"sourceCodeEnd":139,"githubUrl":"https://github.com/Yeachan-Heo/oh-my-codex/blob/3ad79a8a6fe6e95fdbb8c00e40716fffe4011ce2/src/mcp/state-paths.ts#L103-L139","documentation":"validateSessionId in state-paths throws when session_id is provided but is not a string (number, object, array, boolean). Session IDs are used to build state directory names, so they must be strings matching a strict pattern.","triggerScenarios":"Passing session_id: 12345 (number from a JSON producer that dropped quotes), session_id: {id: \"x\"}, or an array. null/undefined is allowed and returns undefined; any other non-string type throws.","commonSituations":"JSON serialization losing string quoting for numeric-looking IDs; forwarding IDs from a database as numbers; LLM tool calls emitting numbers for IDs.","solutions":["Convert the ID to a string before calling: String(sessionId)","Fix the producer so session_id is serialized as a JSON string","Add a type guard on the boundary of your client code"],"exampleFix":"// before\nvalidateSessionId(12345);\n// after\nvalidateSessionId(String(12345)); // \"12345\"","handlingStrategy":"type-guard","validationCode":"if (sessionId != null && typeof sessionId !== 'string') sessionId = String(sessionId);","typeGuard":"function isSessionIdInput(v: unknown): v is string | undefined | null { return v == null || typeof v === 'string'; }","tryCatchPattern":"try { validateSessionId(id); } catch (e) { if ((e as Error).message === 'session_id must be a string') id = String(id); else throw e; }","preventionTips":["Coerce IDs to strings where they originate (DB, URL params)","Stringify numeric IDs at the JSON boundary"],"tags":["type-validation","session-id","mcp"],"backgroundTag":"wrong-argument-type","analyzedSha":"3ad79a8a6fe6e95fdbb8c00e40716fffe4011ce2","analyzedAt":"2026-08-27T22:18:39.783Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}