santifer/career-ops · error · Error

${describeGitCommand(args)} timed out after ${timeoutSeconds

Error message

${describeGitCommand(args)} timed out after ${timeoutSeconds(timeout)}s. If your network is slow, retry or set ${gitTimeoutEnvVar(args)} to a larger value.

What it means

gitIn() runs a git subcommand via execFileSync with a configurable timeout (CAREER_OPS_GIT_TIMEOUT_MS, or CAREER_OPS_GIT_FETCH_TIMEOUT_MS for fetch). If the child does not exit within the timeout, execFileSync kills it and the error is detected as timeout-like; gitIn throws an Error naming the command, the elapsed seconds, and the env var to raise. This prevents update-system.mjs from hanging on a slow network.

Source

Thrown at update-system.mjs:519

function isTimeoutLikeError(err) {
  return err?.code === 'ETIMEDOUT' || err?.signal === 'SIGTERM';
}

function timeoutSeconds(timeout) {
  return Math.round(timeout / 1000);
}

function gitTimeoutEnvVar(args) {
  return args[0] === 'fetch' ? 'CAREER_OPS_GIT_FETCH_TIMEOUT_MS' : 'CAREER_OPS_GIT_TIMEOUT_MS';
}

export function gitIn(root, ...args) {
  const timeout = gitTimeoutMs(args);
  try {
    return execFileSync('git', args, { cwd: root, encoding: 'utf-8', timeout }).trim();
  } catch (err) {
    if (isTimeoutLikeError(err)) {
      throw new Error(`${describeGitCommand(args)} timed out after ${timeoutSeconds(timeout)}s. If your network is slow, retry or set ${gitTimeoutEnvVar(args)} to a larger value.`);
    }
    throw err;
  }
}

function git(...args) {
  return gitIn(ROOT, ...args);
}

/**
 * git(), but with the child's stderr piped instead of inherited.
 *
 * execFileSync inherits stderr by default, so a command whose failure is
 * expected and handled still prints git's raw error to the console. Use this
 * where a non-zero exit is a normal outcome the caller reports itself.
 *
 * @param {...string} args - git arguments.
 * @returns {string} Trimmed stdout.

View on GitHub (pinned to 9b17a8ac97)

Solutions

  1. Raise the timeout via env var: CAREER_OPS_GIT_FETCH_TIMEOUT_MS=300000 node update-system.mjs apply (5 min).
  2. Check connectivity to the remote: git ls-remote <url> to isolate network vs. auth issues.
  3. If a credential prompt is hanging, configure a non-interactive credential helper or cache.
  4. Retry when the network recovers; for a one-time large fetch, run git fetch manually first.

Example fix

# before: default timeout too short for slow link
node update-system.mjs apply  # -> timed out after 60s

# after: raise the fetch timeout
CAREER_OPS_GIT_FETCH_TIMEOUT_MS=300000 node update-system.mjs apply
Defensive patterns

Strategy: retry

Validate before calling

function resolveGitTimeout(args) {
  return args[0] === 'fetch'
    ? Number(process.env.CAREER_OPS_GIT_FETCH_TIMEOUT_MS) || 60_000
    : Number(process.env.CAREER_OPS_GIT_TIMEOUT_MS) || 60_000;
}
if (resolveGitTimeout(['fetch']) < 120000) {
  console.warn('Consider raising CAREER_OPS_GIT_FETCH_TIMEOUT_MS on slow networks');
}

Try / catch

try {
  await runUpdate();
} catch (err) {
  if (err.message.includes('timed out')) {
    const m = err.message.match(/set (\S+) to a larger value/);
    console.error(`${err.message}\nRetrying with larger timeout.`);
    if (m) process.env[m[1]] = String(300000);
    await runUpdate();
  } else throw err;
}

Prevention

When it happens

Trigger: Running update-system.mjs (which calls git fetch/pull/clone) over a slow or stalled network connection that exceeds the default timeout; a hung git credential prompt blocking stdin; an unreachable git remote timing out at TCP level.

Common situations: Slow/constrained network (mobile, throttled CI, cross-continent git host); a credential helper that blocks waiting for input with stdio not inherited; a very large fetch with many objects on a slow link; a temporarily down git hosting provider.

Understand the failure class

Related errors


AI-assisted analysis of santifer/career-ops@9b17a8ac97 (2026-08-13). Data as JSON: /api/errors/483a6f7b8ddc0750. Report an issue: GitHub.