santifer/career-ops · warning

⚠️ Failed to sync PDF flags: ${e.message}

Error message

⚠️  Failed to sync PDF flags: ${e.message}

What it means

After a successful merge (tracker lock already released), merge-tracker.mjs runs sync-pdf-flags.mjs as a child process via execFileSync('node', ...) to reconcile the tracker's PDF ✅/❌ flags against files in output/. If that child exits non-zero or cannot spawn, the error is caught and downgraded to a warning so the completed merge is not rolled back -- but PDF flags may be stale until the next successful sync.

Source

Thrown at merge-tracker.mjs:1372

  // is written.
  if (!existsSync(MERGED_DIR)) mkdirSync(MERGED_DIR, { recursive: true });
  const archivable = tsvFiles.filter(f => !failedAdditions.includes(f));
  for (const file of archivable) {
    renameSync(join(ADDITIONS_DIR, file), join(MERGED_DIR, file));
  }
  console.log(`\n✅ Moved ${archivable.length} TSVs to merged/`);
}

console.log(`\n📊 Summary: +${added} added, 🔄${updated} updated, ⏭️${skipped} skipped${failedAdditions.length ? `, ❌${failedAdditions.length} NOT merged` : ''}`);
if (DRY_RUN) console.log('(dry-run — no changes written)');
trackerLock.release();

// Sync PDF flags (idempotent; uses its own lock/transaction)
if (!DRY_RUN) {
  try {
    execFileSync('node', [join(CAREER_OPS, 'sync-pdf-flags.mjs')], { stdio: 'inherit' });
  } catch (e) {
    console.warn(`⚠️  Failed to sync PDF flags: ${e.message}`);
  }
}

// Optional verify
if (VERIFY && !DRY_RUN) {
  console.log('\n--- Running verification ---');
  try {
    execFileSync('node', [join(CAREER_OPS, 'verify-pipeline.mjs')], { stdio: 'inherit' });
  } catch (e) {
    process.exit(1);
  }
}

// Any addition that could not be applied fails the run. The TSVs stay in the
// additions dir, so re-running after the tracker is repaired merges them once.
if (failedAdditions.length > 0) {
  console.error(
    `\n❌ ${failedAdditions.length} addition(s) were NOT merged and were left in ${ADDITIONS_DIR}: ` +

View on GitHub (pinned to 60398d6549)

Solutions

  1. Run node sync-pdf-flags.mjs manually in the repo root and read the real error message it prints.
  2. Confirm 'node -v' works in the same shell/environment that ran merge-tracker.
  3. Fix the underlying cause (permissions, output/ state), then re-run node merge-tracker.mjs or just the sync -- both are idempotent.

Example fix

# before: only seeing the downgraded warning
⚠️  Failed to sync PDF flags: spawn node ENOENT
# after: reproduce with full output and fix PATH
$ node -v || export PATH="$HOME/.nvm/versions/node/$(cat .nvmrc)/bin:$PATH"
$ node sync-pdf-flags.mjs   # shows the actual failure
Defensive patterns

Strategy: retry

Validate before calling

// Before merge, confirm the sync child can run in this environment
import { existsSync } from 'node:fs';
import { execFileSync } from 'node:child_process';
if (!existsSync('sync-pdf-flags.mjs')) throw new Error('sync-pdf-flags.mjs missing from checkout');
execFileSync('node', ['-v'], { stdio: 'ignore' }); // fails fast if node is unresolvable

Try / catch

// After merge: if the auto-sync warned, re-run it manually and surface the real error
try {
  execFileSync('node', ['sync-pdf-flags.mjs'], { stdio: 'inherit' });
} catch (e) {
  console.error(`PDF flag sync failed (${e.status ?? e.message}) — run 'node sync-pdf-flags.mjs' to see the cause`);
}

Prevention

When it happens

Trigger: sync-pdf-flags.mjs crashing on a corrupt/locked tracker or unreadable output/ directory; 'node' not resolvable from the merge-tracker process environment (PATH shims, tman wrappers); CAREER_OPS pointing at a checkout where sync-pdf-flags.mjs is missing or not executable.

Common situations: PATH-shim environments that reroute node; a repo updated mid-session leaving script mismatch; permission or disk errors in output/; concurrent process holding sync-pdf-flags' own lock/transaction.

Related errors


AI-assisted analysis of santifer/career-ops@60398d6549 (2026-08-20). Data as JSON: /api/errors/8cff5aa6aca35dc8. Report an issue: GitHub.