{"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":"Error","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/8321021c54d670126ce3b2969d5deb880b4b0c2a/scripts/lib/skill-improvement/observations.js#L5-L41","documentation":"ensureString is a shared guard in the observations telemetry module: the value must be a string whose trimmed length is non-zero, otherwise the error is thrown with the parameter label embedded. It returns the trimmed value. It is used to validate identifiers like task and skillId before writing observation records.","triggerScenarios":"Calling observation APIs with task or skillId set to undefined, null, an empty string, or a whitespace-only string like '   '; also passing a non-string (number, object) which fails the typeof check.","commonSituations":"Optional config field left unset so the value stays undefined; template-literal interpolation producing '' when an upstream variable is empty; extracting task names from a CLI flag the user never supplied; refactors changing a field name so the old variable no longer exists.","solutions":["Provide a non-empty string for the flagged field (see label in the message)","Trim and check the value before calling: if (!value || !value.trim()) handle it upstream","Fix the config/CLI plumbing so the field is actually populated","If the field is genuinely optional, pass a sensible default like 'unknown' instead of an empty string"],"exampleFix":"// before\nrecordObservation({ task: cfg.taskName ?? '', skillId }); // throws when unset\n// after\nconst task = (cfg.taskName ?? '').trim();\nif (!task) throw new Error('taskName is required in config');\nrecordObservation({ task, skillId });","handlingStrategy":"type-guard","validationCode":"function isNonEmptyString(v) {\n  return typeof v === 'string' && v.trim().length > 0;\n}\nif (!isNonEmptyString(task) || !isNonEmptyString(skillId)) {\n  throw new Error('task and skillId are required non-empty strings');\n}","typeGuard":"const isNonEmptyString = (v) => typeof v === 'string' && v.trim().length > 0;","tryCatchPattern":"try {\n  recordObservation({ task, skillId });\n} catch (err) {\n  if (err.message.endsWith('must be a non-empty string')) {\n    console.warn(`Skipping observation: ${err.message}`);\n    return null;\n  } throw err;\n}","preventionTips":["Validate CLI flags and config fields for emptiness at program start","Never use '' as a default for required identifiers — leave undefined and fail early","Trim values read from env vars and config files before use","Add schema validation (e.g. zod) for telemetry inputs"],"tags":["validation","strings"],"backgroundTag":"empty-required-field","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"}