{"record":{"id":"a6507207a2547f18","repo":"affaan-m/ECC","slug":"canonical-session-snapshot-requires-fieldpath-t-a65072","errorCode":null,"errorMessage":"Canonical session snapshot requires ${fieldPath} to be a string or null","messagePattern":"Canonical session snapshot requires (.+?) to be a string or null","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"scripts/lib/session-adapters/canonical-session.js","lineNumber":47,"sourceCode":"    .map(line => line.trim())\n    .filter(Boolean);\n}\n\nfunction ensureString(value, fieldPath) {\n  if (typeof value !== 'string' || value.length === 0) {\n    throw new Error(`Canonical session snapshot requires ${fieldPath} to be a non-empty string`);\n  }\n}\n\nfunction ensureStringAllowEmpty(value, fieldPath) {\n  if (typeof value !== 'string') {\n    throw new Error(`Canonical session snapshot requires ${fieldPath} to be a string`);\n  }\n}\n\nfunction ensureOptionalString(value, fieldPath) {\n  if (value !== null && value !== undefined && typeof value !== 'string') {\n    throw new Error(`Canonical session snapshot requires ${fieldPath} to be a string or null`);\n  }\n}\n\nfunction ensureBoolean(value, fieldPath) {\n  if (typeof value !== 'boolean') {\n    throw new Error(`Canonical session snapshot requires ${fieldPath} to be a boolean`);\n  }\n}\n\nfunction ensureArrayOfStrings(value, fieldPath) {\n  if (!Array.isArray(value) || value.some(item => typeof item !== 'string')) {\n    throw new Error(`Canonical session snapshot requires ${fieldPath} to be an array of strings`);\n  }\n}\n\nfunction ensureInteger(value, fieldPath) {\n  if (!Number.isInteger(value) || value < 0) {\n    throw new Error(`Canonical session snapshot requires ${fieldPath} to be a non-negative integer`);","sourceCodeStart":29,"sourceCodeEnd":65,"githubUrl":"https://github.com/affaan-m/ECC/blob/01e15490f04e29cfefe3896951f43db46994d8ee/scripts/lib/session-adapters/canonical-session.js#L29-L65","documentation":"Thrown by the ensureOptionalString helper when a field that may be null/undefined or a string is instead some other type. Applied to session.repoRoot, workers[i].branch, workers[i].worktree, and workers[i].runtime.command — these are optional but, when present, must be strings.","triggerScenarios":"A worker.branch set to a number or boolean; session.repoRoot set to an object; runtime.command set to null incorrectly typed as something else. Typically an adapter forwards a source field that exists but is not a string.","commonSituations":"A source session record where branch is a numeric id; a transform that wraps repoRoot in an object; mixed data from an adapter whose source schema differs from the canonical one.","solutions":["Normalize optional fields to either a string or null before building the snapshot.","Use a helper: const optStr = v => (typeof v === 'string' ? v : v == null ? null : String(v)).","Drop fields you cannot confidently type as string|null."],"exampleFix":"// before\nsession: { repoRoot: metadata.worktree, ... } // worktree is an object\n\n// after\nconst toOptStr = v => (typeof v === 'string' ? v : null);\nsession: { repoRoot: toOptStr(metadata.worktree), ... }","handlingStrategy":"type-guard","validationCode":"const optStr = v => (typeof v === 'string' ? v : null);\n// apply to repoRoot, branch, worktree, runtime.command","typeGuard":"function isOptionalString(v) {\n  return v === null || v === undefined || typeof v === 'string';\n}","tryCatchPattern":null,"preventionTips":["Normalize optional fields to string-or-null at the adapter boundary.","Do not forward source fields of unknown type directly."],"tags":["validation","session-schema","canonical-session"],"backgroundTag":null,"analyzedSha":"01e15490f04e29cfefe3896951f43db46994d8ee","analyzedAt":"2026-08-13T00:31:08.655Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}