santifer/career-ops · warning
⚠️ Additions carry a URL but this tracker has no URL…
Error message
⚠️ Additions carry a URL but this tracker has no URL column — URL dedup is INACTIVE (fuzzy fallback). Add a `URL` header column to enable it.
What it means
URL-based dedup (Pass 0) needs a URL column in the tracker header to store and compare normalized posting URLs. When additions carry URLs but the tracker lacks the column, merge-tracker.mjs cannot engage the deterministic tier and dedup silently degrades to fuzzy company+role matching — so it warns once (warnedNoUrlCol) instead of degrading invisibly.
Solutions
- Add a 'URL' header column to data/applications.md (empty for old rows is fine) and re-run the merge.
- Run node merge-tracker.mjs --backfill-urls to backfill URLs from existing tracker reports, then merge.
- Accept fuzzy dedup if your pipeline has no duplicate URLs — the warning is informational.
- Standardize batch workers to always emit the url column so dedup stays deterministic.
Example fix
# before (tracker header) | # | Date | Company | Role | Score | Status | PDF | Report | Notes | # after | # | Date | Company | Role | Score | Status | PDF | Report | Notes | URL |
Defensive patterns
Strategy: validation
Validate before calling
const trackerHeader = fs.readFileSync('data/applications.md','utf8').split('\n')[0];
if (addition.url && !/\bURL\b/i.test(trackerHeader)) console.warn('Add a `URL` header column to data/applications.md to enable URL dedup'); Prevention
- Include a URL column in the tracker header from day one.
- Emit the url field in every batch TSV so deterministic Pass 0 dedup engages.
- Run node merge-tracker.mjs --backfill-urls after adding the column to hydrate old rows.
- Re-check the header after any manual tracker edit.
When it happens
Trigger: Any merge where addition.url is set (the TSV's url column or a trailing http(s):// field) and COLMAP.url is null — i.e. data/applications.md has no 'URL' header column.
Common situations: Tracker created before the URL column feature; a hand-maintained minimal tracker; an old template lacking the column — same-position reposts then dedup poorly.
Related errors
- ⚠️ Skipping : cannot tell score from status in columns 5–6…
- ⚠️ : carries via= but the tracker has no Via column — value…
- ⚠️ : ignoring unrecognized header column(s)
- follow-ups.md: of rows will be skipped by stats.mjs and…
- ⚠️ Non-canonical status
AI-assisted analysis of santifer/career-ops@aac998c7ed (2026-09-16).
Data as JSON: /api/errors/dc04c8c68f9fad72.
Report an issue: GitHub.
Appendix: source
Thrown at merge-tracker.mjs:1378
const content = readFileSync(join(ADDITIONS_DIR, file), 'utf-8');
const addition = parseTsvContent(content, file);
if (!addition) { skipped++; continue; }
// A via= tag can only be stored if the tracker has a Via column — warn
// instead of dropping the channel silently (#1596). Clear the value too:
// existing rows parse with via='' on this layout, so a set addition.via would
// make the cross-channel duplicate guard see a channel mismatch and add a
// second ? row instead of updating the same-agency re-blast.
if (addition.via && COLMAP.via == null) {
console.warn(`⚠️ ${file}: carries via=${addition.via} but the tracker has no Via column — value dropped. Add it with: node merge-tracker.mjs --migrate-via`);
addition.via = '';
}
// If additions carry a URL but the tracker has no URL
// column, deterministic Pass 0 cannot engage and dedup silently falls back to
// the fuzzy tiers. Warn once rather than degrade invisibly.
if (addition.url && COLMAP.url == null && !warnedNoUrlCol) {
console.warn('⚠️ Additions carry a URL but this tracker has no URL column — URL dedup is INACTIVE (fuzzy fallback). Add a `URL` header column to enable it.');
warnedNoUrlCol = true;
}
// Normalize the report link to be relative to the tracker file's directory.
// The TSV convention carries a root-relative `reports/...` link; rewrite it
// so it resolves correctly when clicked from applications.md (see #760).
addition.report = normalizeReportLink(addition.report);
// Derive the key from the linked report when the TSV did not carry one, so a
// row added through the normal nine-column flow is born WITH its key. This
// must run before the dedup below reads addition.url — deriving it afterwards
// would populate the column while leaving Pass 0 nothing to match on, which is
// the original bug with extra steps.
if (!addition.url) addition.url = resolveReportUrl(addition.report).url;
// Check for duplicate by:
// 0. Exact normalized posting URL (deterministic, authoritative)
// 1. Exact report number matchView on GitHub (pinned to aac998c7ed)