{"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":"validation","errorClass":null,"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/8321021c54d670126ce3b2969d5deb880b4b0c2a/scripts/lib/session-adapters/canonical-session.js#L41-L77","documentation":"ensureArrayOfStrings requires the field to be an Array whose every element is a string. This error fires when the field is not an array at all or contains at least one non-string element. It guards list-shaped snapshot fields (e.g. tags, worker ids) so consumers can safely .map over strings.","triggerScenarios":"A snapshot passed to validateCanonicalSnapshot (via persistCanonicalSnapshot or normalize* adapters) has a list field set to a string, object, null, or an array containing numbers/nulls/objects.","commonSituations":"JSON config where a single value was given instead of a list (\"tags\": \"a\" vs \"tags\": [\"a\"]); deserializing a Set (non-JSON) into the snapshot; mixing numeric ids into a string array from another tool's output.","solutions":["Ensure the field is an array: wrap single values ([value]) if a scalar was supplied","Map elements to strings (arr.map(String)) or filter out non-strings if that is semantically safe","Fix the upstream producer to emit string[] JSON","Regenerate the snapshot via the correct normalize* adapter"],"exampleFix":"// before\nsnapshot.tags = 'primary';\n// after\nsnapshot.tags = ['primary'];","handlingStrategy":"validation","validationCode":"const okArr = (v) => Array.isArray(v) && v.every((x) => typeof x === 'string');","typeGuard":"const isArrayOfStrings = (v) => Array.isArray(v) && v.every((x) => typeof x === 'string');","tryCatchPattern":"try { persistCanonicalSnapshot(s); } catch (e) { if (/array of strings/.test(e.message)) { /* wrap or map */ } else throw e; }","preventionTips":["Emit arrays for list fields","Convert Sets to Array before serializing"],"tags":["validation","schema","array"],"backgroundTag":"schema-validation-failed","analyzedSha":"8321021c54d670126ce3b2969d5deb880b4b0c2a","analyzedAt":"2026-09-16T10:08:13.343Z","contentChangedAt":"2026-09-16T10:08:13.343Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}