santifer/career-ops · error
⚠️ Skipping : header is missing required column(s)
Error message
⚠️ Skipping ${filename}: header is missing required column(s): ${missing.join(', ')} What it means
parseHeadedAddition() validates that every REQUIRED column label (num, date, company, role, score, status, pdf, report) is present in the TSV header. If any are missing, the file is skipped entirely with this warning listing the absent labels — a malformed addition is never merged partially.
Solutions
- Add the missing required column(s) named in the warning to the header row and fill a value in the data row (use a sentinel like N/A for score when unscored)
- Copy the header template from AGENTS.md / DATA_CONTRACT.md rather than writing it by hand
- Re-run merge-tracker.mjs after fixing
Example fix
// before num\tdate\tcompany\trole\tscore\tstatus\tnotes // after num\tdate\tcompany\trole\tscore\tstatus\tpdf\treport\tnotes
Defensive patterns
Strategy: validation
Validate before calling
const REQUIRED = ['num','date','company','role','score','status','pdf','report'];
const present = headerRow.split('\t').map(s => s.trim().toLowerCase());
const missing = REQUIRED.filter(r => !present.includes(r));
if (missing.length) throw new Error('Missing required columns: ' + missing.join(', ')); Prevention
- Start every tracker-addition file from the documented 10-label header
- Include all required columns even when a value is a sentinel (N/A)
- Run merge-tracker.mjs and read its warnings before considering a batch merged
When it happens
Trigger: A batch/tracker-additions/*.tsv whose header omits one or more required labels — e.g. forgetting the pdf column, or writing only a partial header row.
Common situations: Hand-written TSV files missing columns like pdf or report; trimmed-down headers someone assumed were optional; copy-paste that dropped a column.
Understand the failure class
Background: "is required", "must be set", "missing required field": configuration validation errors across open-source libraries — this error's family across 36 libraries.
Related errors
- ⚠️ : ignoring unrecognized header column(s)
- ⚠️ Skipping : header labels the same column twice ( ) —…
- # : unknown employer (?) but the tracker has no Via column…
- ⚠️ Non-canonical status
- ⚠️ Skipping : ambiguous extra fields [ ] — expected at most…
AI-assisted analysis of santifer/career-ops@aac998c7ed (2026-09-16).
Data as JSON: /api/errors/3dfa85b0501703a8.
Report an issue: GitHub.
Appendix: source
Thrown at merge-tracker.mjs:788
* score/status pair needs no content sniffing.
*
* This is the form that has no undecidable case. `resolveScoreStatus` cannot
* order a discarded, never-scored row (`—` is both a score sentinel and a
* status meaning Discarded), so the headerless path refuses it; a header row
* answers it outright (#3517).
*
* @param {string[]} lines - Non-empty lines of the addition file.
* @param {string} filename - Source filename used in warning messages.
* @returns {object|null} Parsed tracker addition, or null when malformed.
*/
function parseHeadedAddition(lines, filename) {
const { map, missing, duplicates, unknown } = resolveTsvColumns(splitAdditionCells(lines[0]));
if (duplicates.length) {
console.warn(`⚠️ Skipping ${filename}: header labels the same column twice (${[...new Set(duplicates)].join(', ')}) — refusing to guess which one carries the value`);
return null;
}
if (missing.length) {
console.warn(`⚠️ Skipping ${filename}: header is missing required column(s): ${missing.join(', ')}`);
return null;
}
if (unknown.length) {
// Not fatal: an unrecognized label is a column this version has no meaning
// for, and dropping it loses nothing the tracker could store anyway. Say so
// rather than swallowing it, since the usual cause is a typo'd label.
console.warn(`⚠️ ${filename}: ignoring unrecognized header column(s): ${unknown.join(', ')}`);
}
const dataLines = lines.slice(1);
if (dataLines.length === 0) {
console.warn(`⚠️ Skipping ${filename}: header row with no data row`);
return null;
}
if (dataLines.length > 1) {
// One addition per file is the contract every caller assumes (the merge
// loop takes one addition per TSV). Rejecting is louder than merging the
// first row and dropping the rest, which would lose evaluations silently.View on GitHub (pinned to aac998c7ed)