JuliusBrussee/caveman · error · Error

sync incomplete — ${failures.join("; ")}

Error message

sync incomplete — ${failures.join("; ")}

What it means

Thrown by the CLI's explicit sync path after it fans local-state uploads out into parallel lanes (practice findings, local scan) using allSettled-style handling. Each rejected lane appends a human-readable '<lane>: <reason>' string; if any lane failed, one aggregated error is thrown so partial success is never reported as a complete sync. The nearby syncAfterLogin helper deliberately does NOT throw (best-effort after login), so hitting this message means the user-invoked sync command ran.

Source

Thrown at packages/cli/src/index.ts:10150

      if (out.firstSync) console.log(SYNC_DISCLOSURE);
      console.log(syncSavingsLine(out));
    }
  } else {
    failures.push(`local spans: ${syncLaneError(savingsLane.reason)}`);
  }
  if (practicesLane.status === "fulfilled") {
    if (practicesLane.value.kind === "synced") {
      console.log(`synced ${practicesLane.value.findings} local practice findings · basis: inferred (tokens only; no payload evidence)`);
    }
  } else {
    failures.push(`practice findings: ${syncLaneError(practicesLane.reason)}`);
  }
  if (localScanLane.status === "fulfilled") {
    if (localScanLane.value.kind === "synced") console.log(localScanSyncLine(localScanLane.value));
  } else {
    failures.push(`local scan: ${syncLaneError(localScanLane.reason)}`);
  }
  if (failures.length > 0) throw new Error(`sync incomplete — ${failures.join("; ")}`);
}

function syncLaneError(reason: unknown): string {
  return reason instanceof Error ? reason.message : String(reason);
}

// syncAfterLogin runs the same sync once right after a successful login so the
// dashboard immediately shows what the local proxy already measured. Strictly
// best-effort: a sync failure never fails the login.
async function syncAfterLogin() {
  try {
    const cfg = await config();
    if (!cfg.token) return;
    const [savingsLane, practicesLane, localScanLane] = await Promise.allSettled([
      syncLocalSavings(cfg),
      syncLocalPracticeFindings(cfg),
      syncPendingLocalScan(cfg),
    ] as const);

View on GitHub (pinned to 5184b3d11a)

Solutions

  1. Read the joined lane reasons to identify which sync failed (practice findings vs local scan) before changing anything
  2. Verify the local proxy is running and healthy (e.g. `caveman status` or its stats endpoint)
  3. Run `caveman setup` to confirm engine/proxy binaries resolve; set CAVEMAN_ENGINE_BIN if the engine lives elsewhere
  4. If a lane reason mentions 401/403 or token expiry, re-run `caveman login` and retry
  5. Re-run the sync — lane failures caused by transient network errors usually clear on retry
Defensive patterns

Strategy: retry

Validate before calling

// Pre-flight the lanes' shared dependency so transient sync failures never start.
import { spawnSync } from 'node:child_process';
const probe = spawnSync(process.env.CAVEMAN_ENGINE_BIN ?? 'caveman-engine', ['--version']);
if (probe.error) throw new Error(`engine missing: ${String(probe.error.message)}`);

Try / catch

try {
  await runSync();
} catch (e) {
  const msg = (e as Error).message;
  if (msg.startsWith('sync incomplete')) {
    // lane names + reasons are '; '-joined — retry only the failed lanes, or the whole sync once
  } else throw e;
}

Prevention

When it happens

Trigger: Running the sync command when at least one lane rejects: the local proxy/engine subprocess exits non-zero, the proxy is not running or not listening, the engine binary is missing from PATH/CAVEMAN_ENGINE_BIN, or the cloud API call fails on auth or network. The message enumerates every failed lane joined by '; '.

Common situations: Fresh machine that never ran `caveman setup` (engine/proxy binaries absent), proxy daemon stopped or crashed mid-run, expired or rotated auth token, transient network loss during upload, version skew between CLI and local proxy.

Related errors


AI-assisted analysis of JuliusBrussee/caveman@5184b3d11a (2026-08-18). Data as JSON: /api/errors/c85b8a36df7051f0. Report an issue: GitHub.