santifer/career-ops · error
Unknown template format
Error message
Unknown template format: ${format} What it means
stripEmptySections removes optional sections with no payload entries, but it looks up marker patterns via PATTERNS[format] first. If the format string isn't a registered key in PATTERNS (only formats with defined section-marker patterns qualify), it throws instead of guessing, since without patterns it cannot know what to strip.
Solutions
- Pass a registered format ('html' or 'tex') as the third argument.
- Check the PATTERNS map in cv-sections-core.mjs for valid keys.
- Fix case sensitivity ('HTML' → 'html').
- If adding a new format, register a full PATTERNS entry for it first.
Example fix
// before stripEmptySections(template, payload, 'markdown'); // after stripEmptySections(template, payload, 'html');
Defensive patterns
Strategy: validation
Validate before calling
import { PATTERNS } from './cv-sections-core.mjs';
function canStrip(format) {
return Object.prototype.hasOwnProperty.call(PATTERNS, format);
}
// if (!canStrip(format)) throw new Error(`stripEmptySections: unregistered format '${format}'`); Type guard
const hasPatterns = (fmt) => fmt != null && typeof PATTERNS[fmt] === 'object';
Try / catch
try {
out = stripEmptySections(tpl, payload, format);
} catch (e) {
if (e.message.startsWith('Unknown template format:')) {
console.error(`Format '${format}' has no section patterns; returning template unstripped`);
out = tpl;
} else throw e;
} Prevention
- Derive the format value from the same constant list the builders use.
- Register a PATTERNS entry before introducing any new format.
- Test stripEmptySections for each supported format in CI.
- Avoid hand-typing format strings — pass them through from the CLI dispatch table.
When it happens
Trigger: Calling stripEmptySections(template, payload, 'md') or any format other than the registered keys (e.g. 'html'/'tex' as wired into the builders); passing undefined format from an unvalidated option; a typo like 'HTML' if PATTERNS keys are lowercase.
Common situations: Adding a new output format without registering its PATTERNS entry; calling the shared core directly from custom tooling with an ad-hoc format name; case-mismatched format strings from CLI parsing.
Related errors
- Template missing required placeholders: }}`).join(', ')}
- Unresolved placeholders
- 4dayweek: invalid URL
- 4dayweek: URL must use HTTPS
- a 40-hex commit --sha is required
AI-assisted analysis of santifer/career-ops@aac998c7ed (2026-09-16).
Data as JSON: /api/errors/aba5e1cebe2553e7.
Report an issue: GitHub.
Appendix: source
Thrown at cv-sections-core.mjs:144
projects: new RegExp(String.raw`%{4,}\s+PROJECTS\s+%{4,}[\s\S]*?` + TEX_BOUNDARY),
education: new RegExp(String.raw`%{4,}\s+Education\s+%{4,}[\s\S]*?` + TEX_BOUNDARY),
awards: new RegExp(String.raw`%{4,}\s+AWARDS\s+%{4,}[\s\S]*?` + TEX_BOUNDARY),
skills: new RegExp(String.raw`%{4,}\s+Technical Skills\s+%{4,}[\s\S]*?` + TEX_END_SENTINEL, 'm'),
},
};
export const OPTIONAL_SECTIONS = ['competencies', 'experience', 'projects', 'education', 'certifications', 'awards', 'interests', 'skills'];
export function isEmptySection(payload, section) {
const entries = payload?.[section];
return !Array.isArray(entries) || entries.length === 0;
}
// Remove every optional section that has no entries in `payload`. Returns the
// template unchanged when both are populated.
export function stripEmptySections(template, payload, format) {
const patterns = PATTERNS[format];
if (!patterns) throw new Error(`Unknown template format: ${format}`);
let out = template;
for (const section of OPTIONAL_SECTIONS) {
const pattern = patterns[section];
if (!pattern) continue; // this format's template has no marker for this section
if (isEmptySection(payload, section)) {
// A non-matching pattern is a no-op here by design — see the Skills
// sentinel note above: no sentinel means no strip, never a fallback to
// a looser boundary.
out = out.replace(pattern, '');
}
}
return out;
}
View on GitHub (pinned to aac998c7ed)