{"record":{"id":"10ab8d4a1a2926d7","repo":"can1357/oh-my-pi","slug":"unknown-changelog-category-raw","errorCode":null,"errorMessage":"Unknown changelog category: ${raw}","messagePattern":"Unknown changelog category: (.+?)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/commit/conventional/commit-types.ts","lineNumber":207,"sourceCode":"\tfor (const item of values) {\n\t\tif (typeof item === \"string\") {\n\t\t\tif (item) details.push({ text: item, userVisible: false });\n\t\t\tcontinue;\n\t\t}\n\t\tif (!isRecord(item) || item.text === null || item.text === undefined) continue;\n\t\tconst text = String(item.text);\n\t\tif (!text) continue;\n\t\tconst category =\n\t\t\ttypeof item.changelog_category === \"string\" ? changelogCategory(item.changelog_category) : undefined;\n\t\tconst userVisible = typeof item.user_visible === \"boolean\" ? item.user_visible : false;\n\t\tdetails.push({ text, changelogCategory: userVisible ? category : undefined, userVisible });\n\t}\n\treturn details;\n}\n\nfunction changelogCategory(raw: string): ChangelogCategory {\n\tconst category = CHANGELOG_CATEGORY_BY_NAME[raw.trim().toLowerCase()];\n\tif (!category) throw new Error(`Unknown changelog category: ${raw}`);\n\treturn category;\n}\n\nfunction stringsFrom(value: unknown): string[] {\n\tif (value === null || value === undefined) return [];\n\tif (typeof value === \"string\") {\n\t\tconst trimmed = value.trim();\n\t\tif (trimmed.startsWith(\"[\")) {\n\t\t\ttry {\n\t\t\t\treturn stringsFrom(JSON.parse(trimmed));\n\t\t\t} catch {}\n\t\t}\n\t\treturn value\n\t\t\t.split(/\\r?\\n/)\n\t\t\t.map(line => line.trim())\n\t\t\t.filter(Boolean);\n\t}\n\tif (Array.isArray(value)) return value.flatMap(stringsFrom);","sourceCodeStart":189,"sourceCodeEnd":225,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/commit/conventional/commit-types.ts#L189-L225","documentation":"changelogCategory() maps a raw category string (from model detail objects' changelog_category field) through CHANGELOG_CATEGORY_BY_NAME after trim+lowercase. Unrecognized values throw, because detail entries carry a changelog category that must be one of the fixed ChangelogCategory keys (Added/Changed/Fixed/Removed/Breaking Changes style) used in changelog rendering.","triggerScenarios":"normalizeDetails() encounters a detail with changelog_category set to a string not in the map — e.g. \"improvements\", \"features\", \"deprecated\" (if not configured), typos, or plural/singular variants the map does not alias.","commonSituations":"LLM inventing near-miss category names in structured output; changelog vocabulary changed in the resource while cached model output still uses old names; hand-authored detail JSON with an ad-hoc category.","solutions":["Read the error for the offending raw category and rename it to a configured one (added, changed, fixed, removed, breaking-changes)","Re-run generation so the model emits categories from the documented list","If the category is legitimately new, add it to CHANGELOG_CATEGORY_BY_NAME / the changelog resource","Drop the changelog_category field (set user_visible false) if no category is needed"],"exampleFix":"// before\n{ \"text\": \"faster startup\", \"changelog_category\": \"Performance\", \"user_visible\": true }\n// after\n{ \"text\": \"faster startup\", \"changelog_category\": \"changed\", \"user_visible\": true }","handlingStrategy":"validation","validationCode":"const allowed = new Set([\"added\", \"changed\", \"fixed\", \"removed\", \"breaking changes\"]);\nif (detail.changelog_category && !allowed.has(detail.changelog_category.trim().toLowerCase())) {\n  throw new Error(`Bad category: ${detail.changelog_category}`);\n}","typeGuard":"function isChangelogCategory(raw: string): raw is ChangelogCategory {\n  return raw.trim().toLowerCase() in CHANGELOG_CATEGORY_BY_NAME;\n}","tryCatchPattern":"try {\n  const details = normalizeDetails(raw.details);\n} catch (err) {\n  if (err.message.startsWith(\"Unknown changelog category:\")) {\n    // drop or remap the offending detail's category and retry\n  } else throw err;\n}","preventionTips":["Prompt the model with the exact allowed category names and aliases","Sanitize/normalize categories at the JSON parse boundary, not at render time","Update CHANGELOG_CATEGORY_BY_NAME aliases when the vocabulary changes"],"tags":["changelog","validation","llm-output"],"backgroundTag":"unknown-changelog-category","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}