{"record":{"id":"617c755ed5b6010d","repo":"oven-sh/bun","slug":"azure-operation-poll-failed-response-status","errorCode":null,"errorMessage":"[azure] Operation poll failed: ${response.status} ${await response.text()}","messagePattern":"\\[azure\\] Operation poll failed: (.+?) (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"scripts/azure.mjs","lineNumber":169,"sourceCode":"    const token = await getAccessToken();\n\n    let response;\n    try {\n      response = await fetch(operationUrl, {\n        headers: { Authorization: `Bearer ${token}` },\n      });\n    } catch (err) {\n      fetchErrors++;\n      if (fetchErrors > 10) {\n        throw new Error(`[azure] Operation poll failed after ${fetchErrors} fetch errors`, { cause: err });\n      }\n      console.warn(`[azure] Operation poll fetch error (${fetchErrors}), retrying...`);\n      await new Promise(r => setTimeout(r, 10_000));\n      continue;\n    }\n\n    if (!response.ok) {\n      throw new Error(`[azure] Operation poll failed: ${response.status} ${await response.text()}`);\n    }\n\n    const data = await response.json();\n\n    if (data.status === \"Succeeded\") {\n      return data.properties?.output ?? data;\n    }\n    if (data.status === \"Failed\" || data.status === \"Canceled\") {\n      throw new Error(`[azure] Operation ${data.status}: ${data.error?.message ?? \"unknown\"}`);\n    }\n\n    await new Promise(r => setTimeout(r, 5000));\n  }\n\n  throw new Error(`[azure] Operation timed out after ${maxWaitMs}ms`);\n}\n\n// ============================================================================","sourceCodeStart":151,"sourceCodeEnd":187,"githubUrl":"https://github.com/oven-sh/bun/blob/8c5296ac459e8252d3cd702f3fbcbb0c249d95d5/scripts/azure.mjs#L151-L187","documentation":"A poll request to the Azure async-operation URL completed at the HTTP layer but returned a non-2xx status; the status and body are surfaced. Unlike the fetch-error path, the server answered and rejected the poll — the token is refreshed each iteration, so stale-token 401s are less likely but Authorization failures for the operation URL still land here.","triggerScenarios":"404 when the async operation URL expired (Azure retains operation records only briefly after completion); 401/403 when the identity lacks read on the operation resource; transient 5xx from the poll endpoint not covered by retry.","commonSituations":"Job resumed/retried long after the operation finished, so the operation URL is gone; permission change mid-poll.","solutions":["Read the status and body in the message to classify (404-expired vs 401-permission vs 5xx)","For 404, re-query the target resource's provisioningState instead of the dead operation URL","For 401/403, grant the identity read access to the resource the operation belongs to","Re-run the original operation if it cannot be recovered"],"exampleFix":"// before\nif (!response.ok) {\n  throw new Error(`[azure] Operation poll failed: ${response.status} ${await response.text()}`);\n}\n\n// after\nif (!response.ok) {\n  if (response.status === 404) {\n    console.warn('operation URL expired; falling back to resource state');\n    return { status: 'Unknown' };\n  }\n  throw new Error(`[azure] Operation poll failed: ${response.status} ${await response.text()}`);\n}","handlingStrategy":"retry","validationCode":"null","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Persist the resource id alongside the operation URL so a 404-expired poll can fall back to provisioningState","Poll promptly after submission; Azure retains operation records only briefly after completion","Keep token permissions valid for the whole poll window"],"tags":["azure","http","polling","api"],"backgroundTag":null,"analyzedSha":"8c5296ac459e8252d3cd702f3fbcbb0c249d95d5","analyzedAt":"2026-08-16T08:01:58.794Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}