santifer/career-ops · error · Error
Malformed partial: missing <!--ENTRY-->...<!--/ENTRY--> tags
Error message
Malformed partial: missing <!--ENTRY-->...<!--/ENTRY--> tags
What it means
parsePartial in build-cv-html.mjs extracts an HTML entry from a partial file. It first locates an `<!--ENTRY-->...<!--/ENTRY-->` zone with a regex; if the regex finds no match, it throws this error. The library requires every partial to explicitly delimit the entry region so arbitrary surrounding content (comments, build notes) is never stripped or interpreted.
Solutions
- Wrap the partial's entry content in literal `<!--ENTRY-->` and `<!--/ENTRY-->` tags.
- Verify exact spelling and casing of the tags (uppercase, no internal whitespace: `<!--ENTRY-->`).
- Confirm both opening and closing tags are present and not nested inside another comment.
- Check the file wasn't transformed (minified/comment-stripped) before reaching parsePartial.
Example fix
<!-- before: bare partial -->
<h2>Experience</h2>
{{EXPERIENCE_ITEMS}}
<!-- after: delimited partial -->
<!--ENTRY-->
<h2>Experience</h2>
{{EXPERIENCE_ITEMS}}
<!--/ENTRY--> Defensive patterns
Strategy: validation
Validate before calling
function isParseablePartial(source) {
return typeof source === 'string' && /<!--ENTRY-->[\s\S]*?<!--\/ENTRY-->/.test(source);
}
if (!isParseablePartial(raw)) throw new Error('partial lacks ENTRY markers before calling parsePartial'); Type guard
const isPartialWithEntry = (s) => typeof s === 'string' && s.includes('<!--ENTRY-->') && s.includes('<!--/ENTRY-->'); Try / catch
try {
const entry = parsePartial(source);
} catch (e) {
if (e.message.includes('Malformed partial')) {
console.error(`Partial is missing ENTRY markers: ${partialPath}`);
return fallbackEntry;
}
throw e;
} Prevention
- Keep a canonical partial template with the ENTRY markers as a starting point.
- Add a lint/test asserting every *.partial file matches the ENTRY regex.
- Never run comment-stripping/minification on partial sources.
- Document the marker convention where partials are authored.
When it happens
Trigger: Calling parsePartial(source) with a source string that lacks literal `<!--ENTRY-->` and `<!--/ENTRY-->` markers — e.g. a partial written as bare HTML, markers misspelled (`<!-- entry -->`, wrong casing, extra spaces inside the comment), a missing closing tag, or an empty file.
Common situations: A new CV section partial was authored without knowing the marker convention; a templating step or editor stripped the HTML comments; a partial was copied from a file that only contains the inner content; tags placed inside a nested comment or generated dynamically so they don't appear literally in the source.
Understand the failure class
Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- ⚠️ config/profile.yml cv.sections lists
- Application Answers section has
- Could not determine the rendered PDF page count from its…
- HIRED.md is missing its <!-- ENTRIES --> markers
- Missing value for
AI-assisted analysis of santifer/career-ops@e7abd431fc (2026-09-16).
Data as JSON: /api/errors/5651f4ea6c1a390c.
Report an issue: GitHub.
Appendix: source
Thrown at build-cv-html.mjs:212
// replaced with '' (or with the <!--BLOCK_NAME_EMPTY--> fallback if one
// is defined, which the certifications partial uses for alignment).
//
// By keeping documentation outside the ENTRY zone we never need to strip
// arbitrary HTML comments from the entry template, which avoids the
// CodeQL js/incomplete-multi-character-sanitization rule entirely.
// Parse a partial file and return:
// { entryTemplate: string, blocks: Map<name, {present: string, absent: string}> }
//
// The entry template is extracted from inside <!--ENTRY-->...<!--/ENTRY-->.
// Named conditional-block definitions are extracted from the same zone and
// removed to leave the clean entry template. No HTML comment stripping is
// performed on arbitrary content (no CodeQL sanitization concern).
function parsePartial(source) {
// Step 1: locate the ENTRY zone.
const entryZoneMatch = /<!--ENTRY-->([\s\S]*?)<!--\/ENTRY-->/.exec(source);
if (!entryZoneMatch) {
throw new Error('Malformed partial: missing <!--ENTRY-->...<!--/ENTRY--> tags');
}
const entryZone = entryZoneMatch[1];
// Step 2: extract named conditional-block definitions from the entry zone.
const blockRe = /<!--([A-Z][A-Z0-9_]+)-->([\s\S]*?)<!--\/\1-->/g;
const blocks = new Map();
// Collect the exact definition strings (open-tag + content + close-tag) so we
// can remove them verbatim from the entry zone in step 3, without needing any
// broad HTML-comment regex (which would trigger CodeQL).
const definitionStrings = [];
// Collect every definition first. The _EMPTY fallbacks are resolved in a
// second pass because a fallback may be defined before the block it belongs
// to, and pairing needs to know which block names exist.
const definitions = new Map();
let m;
while ((m = blockRe.exec(entryZone)) !== null) {
const name = m[1];
const content = m[2];View on GitHub (pinned to e7abd431fc)