{"record":{"id":"e4552d2eed77fadd","repo":"affaan-m/ECC","slug":"canonical-session-snapshot-requires-fieldpath-t-e4552d","errorCode":null,"errorMessage":"Canonical session snapshot requires ${fieldPath} to be an array of strings","messagePattern":"Canonical session snapshot requires (.+?) to be an array of strings","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"scripts/lib/session-adapters/canonical-session.js","lineNumber":59,"sourceCode":"    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`);\n  }\n}\n\nconst STALE_THRESHOLD_MS = 5 * 60 * 1000;\n\nfunction parseUpdatedMs(updated) {\n  if (typeof updated !== 'string' || updated.length === 0) return null;\n  const ms = Date.parse(updated);\n  return Number.isNaN(ms) ? null : ms;\n}\n\nfunction deriveWorkerHealth(rawWorker) {","sourceCodeStart":41,"sourceCodeEnd":77,"githubUrl":"https://github.com/affaan-m/ECC/blob/01e15490f04e29cfefe3896951f43db46994d8ee/scripts/lib/session-adapters/canonical-session.js#L41-L77","documentation":"Thrown by the ensureArrayOfStrings helper when a field that must be an array of strings is not an array or contains non-string elements. Applied to workers[i].intent.seedPaths and the three outputs arrays (summary, validation, remainingRisks).","triggerScenarios":"A outputs.summary set to a string instead of an array; a seedPaths array containing a number or object; a field set to null where an array is required; an adapter that sometimes emits a single string instead of a one-element array.","commonSituations":"A source handoff where summary is a comma-separated string rather than a list; a transform that produced mixed-type array elements; missing field defaulting to null instead of []; version skew between adapter and schema.","solutions":["Default to [] and filter to strings when building: (Array.isArray(x) ? x : []).filter(s => typeof s === 'string').","Ensure source adapters always emit arrays, even single-item ones.","Coerce known string fields into [value] when the source provides a scalar."],"exampleFix":"// before\noutputs: { summary: handoff.summary, ... } // handoff.summary is a string or null\n\n// after\nconst toStrArr = v => (Array.isArray(v) ? v.filter(s => typeof s === 'string') : []);\noutputs: { summary: toStrArr(handoff.summary), ... }","handlingStrategy":"validation","validationCode":"const strArr = v => (Array.isArray(v) ? v.filter(s => typeof s === 'string') : []);\n// apply to seedPaths, summary, validation, remainingRisks","typeGuard":"function isStringArray(v) {\n  return Array.isArray(v) && v.every(s => typeof s === 'string');\n}","tryCatchPattern":null,"preventionTips":["Default array fields to [] and filter to strings at the adapter boundary.","Wrap scalar strings as [value] when the source provides a single item."],"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"}