santifer/career-ops · warning
merge-tracker concurrent write test flaked
Error message
merge-tracker concurrent write test flaked (${e.message}). Retrying once... What it means
test-all.mjs exercises merge-tracker.mjs under concurrent writes, asserting that no tracker row is lost when two merges race. The first transient failure (e.g. a lost row on a loaded machine) logs this warning and retries once; exhausting retries turns it into a hard failure for merge-tracker's write path.
Solutions
- One warning then a pass is the intended tolerance for timing noise — no action needed.
- If it fails both attempts, reproduce with two manual merges against the same tracker and inspect merge-tracker.mjs's lock acquisition and atomic rename.
- Run on a local disk (not NFS/SMB) where file locking behaves predictably.
- Check for stray concurrent test processes (parallel CI shards sharing a working tree) interfering with the lock file.
Example fix
// before (merge-tracker write)
fs.writeFileSync(trackerPath, content);
// after
const lock = acquireLock(trackerPath);
try { fs.writeFileSync(tmpPath, content); fs.renameSync(tmpPath, trackerPath); } finally { lock.release(); } Defensive patterns
Strategy: retry
Validate before calling
// sanity: single-writer merge must succeed before testing concurrency
if (run('node', ['merge-tracker.mjs']) === null) fail('merge-tracker broken; skip concurrency test'); Try / catch
try { assertRowsPresent(merged, expectedRows); } catch (e) { if (retries-- > 0) warn(`flaked (${e.message}), retrying`); else fail(e.message); } Prevention
- Verify merge-tracker uses a lock file plus atomic rename for writes
- Run tests on local disks; network mounts weaken locking
- Avoid parallel CI jobs sharing the same repository checkout
- Escalate to fail() only after retries are exhausted — one warning is expected noise
When it happens
Trigger: The merged tracker after two concurrent merge runs is missing a row (`concurrent tracker merge lost a row`) or the merge process crashes, on an attempt where retries > 0 — warning is emitted and retries decremented.
Common situations: Slow CI runners widening the race window between read-modify-write cycles, shared/network filesystems with unreliable locking, or a real bug in merge-tracker.mjs's locking or atomic-write implementation (fails every attempt).
Related errors
- concurrent reservation test flaked
- Could not claim report slot(s) after retries
- archive render skipped — no Playwright browser in env
- Cannot import generate-pdf.mjs
- Cannot verify tracker lock ownership at
AI-assisted analysis of santifer/career-ops@e7abd431fc (2026-09-16).
Data as JSON: /api/errors/2a397ee0e0aded59.
Report an issue: GitHub.
Appendix: source
Thrown at test-all.mjs:13011
const second = spawnMerge(additionsB, 0);
const [firstResult, secondResult] = await Promise.all([first.result, second.result]);
if (firstResult.code === 0 && secondResult.code === 0) {
pass('concurrent merge processes both exited successfully');
} else {
throw new Error(`concurrent merge process failed: first=${firstResult.code} second=${secondResult.code} stderr=${firstResult.stderr || secondResult.stderr}`);
}
const merged = readFileSync(join(mergeTmp, 'data', 'applications.md'), 'utf-8');
if (merged.includes('Alpha') && merged.includes('Beta')) {
pass('concurrent tracker merges preserve rows from both processes');
} else {
throw new Error(`concurrent tracker merge lost a row: ${merged}`);
}
break;
} catch (e) {
if (retries > 0) {
warn(`merge-tracker concurrent write test flaked (${e.message}). Retrying once...`);
retries -= 1;
} else {
fail(`merge-tracker concurrent write test crashed: ${e.message}`);
break;
}
} finally {
rmSync(mergeTmp, { recursive: true, force: true });
}
}
} catch (e) {
fail(`merge-tracker concurrent write test crashed: ${e.message}`);
}
// ── 12. COLD-START TRIGGER ──────────────────────────────────────
console.log('\n12. Cold-start trigger (deterministic onboarding state)');
try {View on GitHub (pinned to e7abd431fc)