{"record":{"id":"a854103f1d64822f","repo":"affaan-m/ECC","slug":"key-must-be-a-non-empty-string-when-provided","errorCode":null,"errorMessage":"${key} must be a non-empty string when provided","messagePattern":"(.+?) must be a non-empty string when provided","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"scripts/lib/install-manifests.js","lineNumber":240,"sourceCode":"      description: `Install only the ${skillId} skill directory.`,\n      modules: [moduleId],\n      synthetic: true,\n    });\n    componentIds.add(componentId);\n  }\n}\n\nfunction readOptionalStringOption(options, key) {\n  if (\n    !Object.prototype.hasOwnProperty.call(options, key)\n    || options[key] === null\n    || options[key] === undefined\n  ) {\n    return null;\n  }\n\n  if (typeof options[key] !== 'string' || options[key].trim() === '') {\n    throw new Error(`${key} must be a non-empty string when provided`);\n  }\n\n  return options[key];\n}\n\nfunction readModuleTargetsOrThrow(module) {\n  const moduleId = module && module.id ? module.id : '<unknown>';\n  const targets = module && module.targets;\n\n  if (!Array.isArray(targets)) {\n    throw new Error(`Install module ${moduleId} has invalid targets; expected an array of supported target ids`);\n  }\n\n  const normalizedTargets = targets.map(target => (\n    typeof target === 'string' ? target.trim() : ''\n  ));\n\n  if (normalizedTargets.some(target => target.length === 0)) {","sourceCodeStart":222,"sourceCodeEnd":258,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/scripts/lib/install-manifests.js#L222-L258","documentation":"readOptionalStringOption validates optional string options (e.g. projectRoot, homeDir). If the option is provided it must be a non-empty, non-whitespace string; otherwise this error is thrown. Passing null/undefined/'' is fine (treated as absent).","triggerScenarios":"Passing options like `{ projectRoot: 123 }`, `{ homeDir: '   ' }`, or an object/array instead of a string to manifest-loading APIs.","commonSituations":"Variables that are undefined-or-number from CLI parsing; env vars read as numbers; whitespace-only values from untrimmed input; accidental object passed where a path string is expected.","solutions":["Ensure the option value is a non-empty trimmed string before calling, e.g. `String(rawPath ?? '').trim() || undefined`","Fix the source of the value (CLI arg, env var) so it is a string","Coerce numbers to strings explicitly if a path was parsed as a number"],"exampleFix":"// before\nloadInstallManifests({ projectRoot: process.cwd().length });\n// after\nconst root = String(process.cwd());\nif (root.trim()) loadInstallManifests({ projectRoot: root });","handlingStrategy":"validation","validationCode":"function assertOptionalString(v) { return v == null || v === '' ? null : (typeof v === 'string' && v.trim() ? v : (() => { throw new Error('option must be a non-empty string'); })()); }","typeGuard":"const isNonEmptyString = (v) => typeof v === 'string' && v.trim().length > 0;","tryCatchPattern":"try { loadInstallManifests({ projectRoot }); } catch (e) { if (e.message.includes('must be a non-empty string')) { console.error('Fix the option value type:', e.message); } else throw e; }","preventionTips":["Trim and type-check path options sourced from CLI/env","Coerce numeric or object values to strings explicitly","Pass undefined (not whitespace) for absent options"],"tags":["validation","options","type-error"],"backgroundTag":"invalid-argument-value","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"}