{"record":{"id":"de28aac879eb3228","repo":"santifer/career-ops","slug":"skipping-filename-header-row-with-no-data-row","errorCode":null,"errorMessage":"⚠️  Skipping ${filename}: header row with no data row","messagePattern":"⚠️  Skipping (.+?): header row with no data row","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"merge-tracker.mjs","lineNumber":800,"sourceCode":"  const { map, missing, duplicates, unknown } = resolveTsvColumns(splitAdditionCells(lines[0]));\n  if (duplicates.length) {\n    console.warn(`⚠️  Skipping ${filename}: header labels the same column twice (${[...new Set(duplicates)].join(', ')}) — refusing to guess which one carries the value`);\n    return null;\n  }\n  if (missing.length) {\n    console.warn(`⚠️  Skipping ${filename}: header is missing required column(s): ${missing.join(', ')}`);\n    return null;\n  }\n  if (unknown.length) {\n    // Not fatal: an unrecognized label is a column this version has no meaning\n    // for, and dropping it loses nothing the tracker could store anyway. Say so\n    // rather than swallowing it, since the usual cause is a typo'd label.\n    console.warn(`⚠️  ${filename}: ignoring unrecognized header column(s): ${unknown.join(', ')}`);\n  }\n\n  const dataLines = lines.slice(1);\n  if (dataLines.length === 0) {\n    console.warn(`⚠️  Skipping ${filename}: header row with no data row`);\n    return null;\n  }\n  if (dataLines.length > 1) {\n    // One addition per file is the contract every caller assumes (the merge\n    // loop takes one addition per TSV). Rejecting is louder than merging the\n    // first row and dropping the rest, which would lose evaluations silently.\n    console.warn(`⚠️  Skipping ${filename}: ${dataLines.length} data rows — one addition per file`);\n    return null;\n  }\n\n  const parts = splitAdditionCells(dataLines[0]);\n  // Only the REQUIRED cells must actually be there. A row whose trailing\n  // optional value is empty is routinely written without its final tab —\n  // openrouter-runner emits `…\\treport\\t\\n` for an absent note, and an LLM\n  // writer asked for \"leave the last field empty\" often just stops — so\n  // demanding a cell for every label would reject canonical rows. Absent and\n  // empty read the same here, which is what the batch and web prompts already\n  // promise their writers.","sourceCodeStart":782,"sourceCodeEnd":818,"githubUrl":"https://github.com/santifer/career-ops/blob/aac998c7ed7248ea853b720ceeb1fdbeb322fc5d/merge-tracker.mjs#L782-L818","documentation":"merge-tracker.mjs refuses to merge a headed tracker-addition TSV whose first line looks like a header (column labels) but is followed by zero data rows. The header-row-plus-one-data-row shape is the contract parseHeadedAddition (merge-tracker.mjs:781) relies on to resolve every field by name; with no row there is nothing to merge, so it warns and returns null instead of silently merging a stub. This keeps malformed batch output in batch/tracker-additions/ from corrupting applications.md.","triggerScenarios":"A file in batch/tracker-additions/{num}-{company-slug}.tsv contains only the header row (num\\tdate\\tcompany\\t...) with no second line, and merge-tracker.mjs runs over the additions directory. Calling parseTsvContent(content, filename) where content's only non-empty line is recognized by looksLikeTsvHeaderRow.","commonSituations":"A script or LLM writer created the TSV, errored out before writing the data row, and left an orphan header; a template was copied but never filled in; an editor or shell heredoc truncated the file after the first line; the writer emitted the header and intended to append the row later.","solutions":["Open the flagged TSV and write the single data row beneath the header, one tab-separated cell per label in the same order.","Delete the empty headed file if no evaluation actually happened, then regenerate it from the evaluation run.","Re-run the emitter (evaluation worker or batch prompt) that was supposed to write the row, and check its exit status/output this time.","If the row was written without a trailing newline elsewhere, verify the file actually ends with the data line (some writers buffer output)."],"exampleFix":"// before (batch/tracker-additions/042-acme.tsv)\nnum\tdate\tcompany\trole\tscore\tstatus\tpdf\treport\n\n// after\nnum\tdate\tcompany\trole\tscore\tstatus\tpdf\treport\n42\t2026-09-16\tAcme\tSenior Backend Engineer\t4.2/5\tEvaluated\t❌\t[042](reports/042-acme-2026-09-16.md)","handlingStrategy":"validation","validationCode":"const lines = content.split(/\\r?\\n/).filter(l => l.trim() !== '');\nif (lines.length < 2) {\n  throw new Error(`${file}: headed TSV needs a header row plus exactly one data row (got ${lines.length} line(s))`);\n}","typeGuard":"const isWellFormedHeadedTsv = (content) => {\n  const lines = String(content ?? '').split(/\\r?\\n/).filter(l => l.trim() !== '');\n  return lines.length === 2;\n};","tryCatchPattern":null,"preventionTips":["Write the header and data row in a single atomic write, never header-first with the row appended later.","Assert lines.length === 2 in any script that emits tracker-addition TSVs.","Check the emitter's exit status and output size before running merge-tracker.mjs.","List batch/tracker-additions/ and inspect file line counts before merging."],"tags":["tsv","parsing","merge-tracker","empty-file"],"backgroundTag":"empty-result-set","analyzedSha":"aac998c7ed7248ea853b720ceeb1fdbeb322fc5d","analyzedAt":"2026-09-16T06:35:29.214Z","contentChangedAt":"2026-09-16T06:35:29.214Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}