{"record":{"id":"6e5fa0714e1cbd77","repo":"affaan-m/ECC","slug":"label-must-be-a-non-empty-string-6e5fa0","errorCode":null,"errorMessage":"${label} must be a non-empty string","messagePattern":"(.+?) must be a non-empty string","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"scripts/lib/skill-improvement/observations.js","lineNumber":23,"sourceCode":"const os = require('os');\n\nconst OBSERVATION_SCHEMA_VERSION = 'ecc.skill-observation.v1';\n\nfunction resolveProjectRoot(options = {}) {\n  return path.resolve(options.projectRoot || options.cwd || process.cwd());\n}\n\nfunction getSkillTelemetryRoot(options = {}) {\n  return path.join(resolveProjectRoot(options), '.claude', 'ecc', 'skills');\n}\n\nfunction getSkillObservationsPath(options = {}) {\n  return path.join(getSkillTelemetryRoot(options), 'observations.jsonl');\n}\n\nfunction ensureString(value, label) {\n  if (typeof value !== 'string' || value.trim().length === 0) {\n    throw new Error(`${label} must be a non-empty string`);\n  }\n\n  return value.trim();\n}\n\nfunction createObservationId() {\n  return `obs-${Date.now()}-${process.pid}-${Math.random().toString(16).slice(2, 8)}`;\n}\n\nfunction createSkillObservation(input) {\n  const task = ensureString(input.task, 'task');\n  const skillId = ensureString(input.skill && input.skill.id, 'skill.id');\n  const skillPath = typeof input.skill.path === 'string' && input.skill.path.trim().length > 0\n    ? input.skill.path.trim()\n    : null;\n  const success = Boolean(input.success);\n  const error = input.error === null || input.error === undefined ? null : String(input.error);\n  const feedback = input.feedback === null || input.feedback === undefined ? null : String(input.feedback);","sourceCodeStart":5,"sourceCodeEnd":41,"githubUrl":"https://github.com/affaan-m/ECC/blob/01e15490f04e29cfefe3896951f43db46994d8ee/scripts/lib/skill-improvement/observations.js#L5-L41","documentation":"Thrown by ensureString() in the skill-improvement observations module, invoked from createSkillObservation() for the 'task' and 'skill.id' fields. The helper rejects anything that is not a string or is empty/whitespace-only, because observation records must be uniquely attributable to a task and a skill identifier for downstream telemetry grouping. The label in the message identifies which field failed.","triggerScenarios":"Calling createSkillObservation({ task: '', skill: { id: 'foo' } }); calling it with task undefined; passing skill.id as null; passing an object whose skill property is missing entirely (input.skill && input.skill.id short-circuits to undefined); passing a numeric id like 42.","commonSituations":"CLI forwards an optional --task flag that was omitted; an upstream telemetry hook fires before the skill id was resolved; a JSON payload from a queue has null for empty fields instead of empty strings; refactoring renames skill.id to skill.name and forgets the call site.","solutions":["Ensure input.task is a non-empty trimmed string before calling createSkillObservation.","Ensure input.skill exists and input.skill.id is a non-empty trimmed string.","Default or reject early at your API boundary: if (!task || !skill?.id) return null; rather than letting ensureString throw deep inside the library.","Add a runtime type check (e.g. zod schema) for the observation payload at ingress."],"exampleFix":"// before\ncreateSkillObservation({ task: '', skill: { id: 'tdd-workflow' }, success: true });\n// -> task must be a non-empty string\n\n// after\nif (!input.task?.trim() || !input.skill?.id?.trim()) return null;\ncreateSkillObservation({\n  task: input.task.trim(),\n  skill: { id: input.skill.id.trim(), path: input.skill.path },\n  success: true,\n});","handlingStrategy":"validation","validationCode":"function isValidObservationInput(input) {\n  return !!(\n    input &&\n    typeof input.task === 'string' && input.task.trim().length > 0 &&\n    input.skill &&\n    typeof input.skill.id === 'string' && input.skill.id.trim().length > 0\n  );\n}\n\nif (!isValidObservationInput(input)) return null;\ncreateSkillObservation(input);","typeGuard":"function isNonEmptyString(value) {\n  return typeof value === 'string' && value.trim().length > 0;\n}\n\nfunction isObservationInput(value) {\n  return !!(\n    value &&\n    isNonEmptyString(value.task) &&\n    value.skill &&\n    isNonEmptyString(value.skill.id)\n  );\n}","tryCatchPattern":"try {\n  createSkillObservation(input);\n} catch (error) {\n  if (/must be a non-empty string/.test(error.message)) {\n    // drop the observation rather than crash the telemetry pipeline\n    return null;\n  }\n  throw error;\n}","preventionTips":["Validate task and skill.id at the source (CLI parser, hook payload) rather than relying on the library.","Use a schema (zod) for observation payloads at ingress.","Treat empty task/skill.id as a signal of an upstream bug — log and investigate, do not silently drop in production.","Coerce null to undefined before the call so the && chain in createSkillObservation behaves predictably."],"tags":["skill-improvement","telemetry","validation","string"],"backgroundTag":null,"analyzedSha":"01e15490f04e29cfefe3896951f43db46994d8ee","analyzedAt":"2026-08-13T00:31:08.655Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}