{"record":{"id":"68799f488687d703","repo":"santifer/career-ops","slug":"skipping-filename-data-row-has-parts-length-field-s-missing","errorCode":null,"errorMessage":"⚠️  Skipping ${filename}: data row has ${parts.length} field(s), missing the required cell(s): ${missingRequiredCells.join(', ')}","messagePattern":"⚠️  Skipping (.+?): data row has (.+?) field\\(s\\), missing the required cell\\(s\\): (.+?)","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"merge-tracker.mjs","lineNumber":826,"sourceCode":"    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.\n  //\n  // This is not the defense against a SHIFTED row: an omitted interior cell\n  // slides every later value one column left, which the score corroboration\n  // below catches by content regardless of the row's width.\n  const shortLabels = Object.entries(map).filter(([, i]) => i >= parts.length).map(([k]) => k);\n  const missingRequiredCells = shortLabels.filter(k => TSV_REQUIRED_FIELDS.includes(k));\n  if (missingRequiredCells.length) {\n    console.warn(`⚠️  Skipping ${filename}: data row has ${parts.length} field(s), missing the required cell(s): ${missingRequiredCells.join(', ')}`);\n    return null;\n  }\n\n  const at = (k) => (map[k] != null ? String(parts[map[k]] ?? '').trim() : '');\n  const optional = (k) => {\n    const v = at(k);\n    return PLACEHOLDER_CELL.test(v) ? '' : v;\n  };\n\n  // A required cell that is PRESENT but blank is a writer that did not supply\n  // the value, and blank is never the way to say \"none\" here: every required\n  // field has a documented value, and the \"no data\" cases have sentinels\n  // (`—` / `N/A` for score, `—` for report, `❌` for pdf). Left unchecked, an\n  // empty status reaches validateStatus(''), which warns and returns\n  // \"Evaluated\" — recording a real evaluation state the row never claimed.\n  const blankRequiredCells = TSV_REQUIRED_FIELDS.filter(k => at(k) === '');\n  if (blankRequiredCells.length) {\n    console.warn(`⚠️  Skipping ${filename}: required cell(s) present but empty: ${blankRequiredCells.join(', ')} — use the documented sentinel (— / N/A) rather than a blank cell`);","sourceCodeStart":808,"sourceCodeEnd":844,"githubUrl":"https://github.com/santifer/career-ops/blob/aac998c7ed7248ea853b720ceeb1fdbeb322fc5d/merge-tracker.mjs#L808-L844","documentation":"In a headed TSV, every required field (num, date, company, role, score, status, pdf, report) must have a cell present in the data row. If the row is shorter than a required label's column index, the parser cannot map the row onto the header and skips the file. Optional trailing cells may be absent (absent and empty read the same), but a short row that cuts off a REQUIRED cell is rejected; an interior omission that shifts values is caught separately by the score corroboration.","triggerScenarios":"parseHeadedAddition (merge-tracker.mjs:825) computes shortLabels = labels whose column index >= parts.length, filters to TSV_REQUIRED_FIELDS, and finds a non-empty set — i.e. splitAdditionCells(dataLines[0]) produced fewer tab-separated cells than the position of some required label.","commonSituations":"A writer emitted only 4-7 of the 8 required cells, stopping after notes; a template's header was edited to add a column but the data row wasn't updated; a shell script built the row with a failing variable leaving the tail empty; tabs were converted to spaces by an editor, collapsing cells.","solutions":["Count the tab-separated cells in the data row against the header labels and append the missing required cell(s) named in the warning, in header order.","Use the documented sentinels where a value genuinely doesn't exist: '—' or 'N/A' for score, '—' for report, '❌' for pdf.","Ensure the file is tab-delimited, not comma- or space-delimited; re-save with real tabs if an editor converted them.","Fix the emitter template so it always writes one tab per header label, even when a value is empty."],"exampleFix":"// before (row stops after role — score/status/pdf/report missing)\nnum\tdate\tcompany\trole\tscore\tstatus\tpdf\treport\n42\t2026-09-16\tAcme\tBackend Engineer\n\n// after\nnum\tdate\tcompany\trole\tscore\tstatus\tpdf\treport\n42\t2026-09-16\tAcme\tBackend Engineer\t4.2/5\tEvaluated\t❌\t[042](reports/042.md)","handlingStrategy":"validation","validationCode":"const REQUIRED = ['num','date','company','role','score','status','pdf','report'];\nconst header = lines[0].split('\\t');\nconst cells = lines[1].split('\\t');\nconst missing = REQUIRED.filter(label => header.indexOf(label) >= cells.length);\nif (missing.length) throw new Error(`${file}: data row missing required cell(s): ${missing.join(', ')}`);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Emit exactly one tab per header label — build rows from the header array itself, not by hand.","Never let a shell variable interpolation silently produce an empty cell; fail loudly on unset variables (set -u).","Verify the file is tab-delimited after every editor save (spaces and commas are not cell separators here).","Keep emitter templates and header labels in sync when adding columns."],"tags":["tsv","parsing","merge-tracker","missing-fields"],"backgroundTag":"missing-required-argument","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"}