{"record":{"id":"bbe25a10381350c5","repo":"oven-sh/bun","slug":"res-status-job-raw-log-url","errorCode":null,"errorMessage":"${res.status} ${job.raw_log_url}","messagePattern":"\\$\\{res\\.status\\} \\$\\{job\\.raw_log_url\\}","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"scripts/ci-slowest-tests.ts","lineNumber":106,"sourceCode":"        .replace(/\\s+\\x1b\\[90m\\[attempt #\\d+\\]\\x1b\\[0m\\r*$/, \"\")\n        .replace(/\\r+$/, \"\")\n        .replace(/\\\\/g, \"/\")\n        .trim();\n      out.set(clean, (out.get(clean) ?? 0) + (ts - curStart));\n    }\n    curStart = ts;\n    curName = m[2].replace(/\\r+$/, \"\").replace(/\\\\/g, \"/\").trim();\n  }\n  return out;\n}\n\n// Do NOT use `bk job log` — it hangs indefinitely on some Windows/alpine jobs.\n// Fetching raw_log_url directly with the token works for all of them.\nasync function fetchLog(job: Job): Promise<string> {\n  const path = join(CACHE, `${job.id}.log`);\n  if (existsSync(path)) return readFileSync(path, \"utf8\");\n  const res = await fetch(job.raw_log_url, { headers: { Authorization: `Bearer ${TOKEN}` } });\n  if (!res.ok) throw new Error(`${res.status} ${job.raw_log_url}`);\n  const out = await res.text();\n  writeFileSync(path, out);\n  return out;\n}\n\ntype Agg = { maxMs: number; maxPlat: string; perPlat: Map<string, number> };\nconst agg = new Map<string, Agg>();\n\nlet done = 0;\nconst queue = [...jobs];\nasync function worker() {\n  for (;;) {\n    const job = queue.shift();\n    if (!job) return;\n    try {\n      const log = await fetchLog(job);\n      const plat = platOf(job.name);\n      for (const [file, ms] of parseLog(log)) {","sourceCodeStart":88,"sourceCodeEnd":124,"githubUrl":"https://github.com/oven-sh/bun/blob/8c5296ac459e8252d3cd702f3fbcbb0c249d95d5/scripts/ci-slowest-tests.ts#L88-L124","documentation":"fetchLog() in scripts/ci-slowest-tests.ts downloads a BuildKite job log via job.raw_log_url with a bearer token (deliberately avoiding `bk job log`, which hangs on some Windows/alpine jobs). It throws when that fetch returns non-2xx; successful downloads are cached as <job.id>.log files under the cache dir.","triggerScenarios":"An expired or rotated raw_log_url (BuildKite log links age out), 401 from an invalid or insufficient TOKEN, 429 when fanning out over many jobs, or logs garbage-collected for old builds.","commonSituations":"Re-running the script against an old build whose log links expired; BUILDKITE_API_TOKEN unset or stale; large builds where parallel log fetches trip rate limits.","solutions":["Re-fetch the job list so raw_log_url values are fresh, then retry","Verify the token with bk whoami or a direct curl to api.buildkite.com","On 429: reduce worker concurrency or wait — cached logs make reruns cheap","If logs were GC'd, target a more recent build"],"exampleFix":"# before\n$ bun scripts/ci-slowest-tests.ts\nError: 403 https://buildkite.com/.../log.txt\n\n# after — fresh token, then re-run so job list (and log URLs) refresh\n$ export BUILDKITE_API_TOKEN=<fresh read token>\n$ bun scripts/ci-slowest-tests.ts","handlingStrategy":"retry","validationCode":"const r = await fetch(\"https://api.buildkite.com/v2/user\", { headers: { Authorization: `Bearer ${TOKEN}` } });\nif (!r.ok) throw new Error(`token invalid: ${r.status}`);","typeGuard":"const isLogFetchError = (e: unknown) =>\n  e instanceof Error && /^\\d{3} http/.test(e.message);","tryCatchPattern":"for (let i = 1; ; i++) {\n  try { return await fetchLog(job); }\n  catch (e) {\n    if (!isLogFetchError(e) || i === 3) throw e;\n    if (e.message.startsWith(\"401 \")) throw e; // auth will not fix itself\n    await new Promise(r => setTimeout(r, i * 2000));\n  }\n}","preventionTips":["Always re-list jobs right before fetching logs — never persist raw_log_url","Keep the <job.id>.log cache so reruns are idempotent and cheap","Cap concurrent log fetches to stay under BuildKite rate limits"],"tags":["buildkite","network","ci","logs","auth"],"backgroundTag":null,"analyzedSha":"8c5296ac459e8252d3cd702f3fbcbb0c249d95d5","analyzedAt":"2026-08-16T08:01:58.794Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}