{"record":{"id":"57ea549943a4fc43","repo":"Yeachan-Heo/oh-my-codex","slug":"session-id-must-match-a-za-z0-9-1-64","errorCode":null,"errorMessage":"session_id must match ^[A-Za-z0-9_-]{1,64}$","messagePattern":"session_id must match \\^\\[A-Za-z0-9_-\\](.+?)\\$","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/mcp/state-paths.ts","lineNumber":124,"sourceCode":"export 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 \"..\"');\n  }\n  if (normalized.includes('/') || normalized.includes('\\\\')) {\n    throw new Error('mode must not contain path separators');","sourceCodeStart":106,"sourceCodeEnd":142,"githubUrl":"https://github.com/Yeachan-Heo/oh-my-codex/blob/3ad79a8a6fe6e95fdbb8c00e40716fffe4011ce2/src/mcp/state-paths.ts#L106-L142","documentation":"Thrown when session_id is a string but does not match ^[A-Za-z0-9_-]{1,64}$ — only alphanumerics, underscore and hyphen, at most 64 chars. The pattern exists because session IDs become filesystem path segments in state directories.","triggerScenarios":"session_id containing dots, slashes, spaces, or unicode (\"sess.1\", \"abc/def\"); IDs longer than 64 characters (e.g. full UUIDs with braces or nano-style IDs plus prefix exceeding 64).","commonSituations":"Using UUIDs with dashes is fine, but including curly braces or dots fails; concatenating run metadata into the ID pushing past 64 chars; copy-paste introducing whitespace.","solutions":["Sanitize the ID: strip/replace characters outside [A-Za-z0-9_-]","Truncate or hash long IDs to <=64 chars","Generate IDs as hex/hyphenated tokens (crypto.randomUUID() passes)"],"exampleFix":"// before\nvalidateSessionId(\"sess.#01/abc\");\n// after\nvalidateSessionId(\"sess-01-abc\");","handlingStrategy":"validation","validationCode":"const SESSION_ID = /^[A-Za-z0-9_-]{1,64}$/;\nif (!SESSION_ID.test(sessionId)) sessionId = sessionId.replace(/[^A-Za-z0-9_-]/g, '').slice(0, 64);","typeGuard":"function isValidSessionId(v: string): boolean { return /^[A-Za-z0-9_-]{1,64}$/.test(v); }","tryCatchPattern":null,"preventionTips":["Generate IDs with crypto.randomUUID() (fits the pattern)","Sanitize+truncate external IDs before use"],"tags":["pattern-validation","session-id","mcp"],"backgroundTag":"id-format-invalid","analyzedSha":"3ad79a8a6fe6e95fdbb8c00e40716fffe4011ce2","analyzedAt":"2026-08-27T22:18:39.783Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}