{"record":{"id":"a272222bd51649b6","repo":"windmill-labs/windmill","slug":"failed-to-load-s3-file-response-status-respo","errorCode":null,"errorMessage":"Failed to load S3 file: ${response.status} ${response.statusText} - ${errorText}","messagePattern":"Failed to load S3 file: (.+?) (.+?) - (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"typescript-client/client.ts","lineNumber":978,"sourceCode":"  }\n  const queryParams = new URLSearchParams(params);\n  const w = workspace ?? getWorkspace();\n\n  // We use raw fetch here b/c OpenAPI generated client doesn't handle Blobs nicely\n  const response = await fetch(\n    `${OpenAPI.BASE}/w/${w}/job_helpers/download_s3_file?${queryParams}`,\n    {\n      method: \"GET\",\n      headers: {\n        Authorization: `Bearer ${OpenAPI.TOKEN}`,\n      },\n    }\n  );\n\n  // Check if the response was successful\n  if (!response.ok) {\n    const errorText = await response.text();\n    throw new Error(\n      `Failed to load S3 file: ${response.status} ${response.statusText} - ${errorText}`\n    );\n  }\n\n  return response.blob();\n}\n\n/**\n * Persist a file to the S3 bucket. If the s3ResourcePath is undefined, it will default to the workspace S3 resource.\n *\n * ```typescript\n * const s3object = await writeS3File(s3Object, \"Hello Windmill!\")\n * const fileContentAsUtf8Str = (await s3object.toArray()).toString('utf-8')\n * console.log(fileContentAsUtf8Str)\n * ```\n *\n * @param workspace - Workspace to write to (defaults to the `WM_WORKSPACE` env var)\n */","sourceCodeStart":960,"sourceCodeEnd":996,"githubUrl":"https://github.com/windmill-labs/windmill/blob/e474e8803ce2ff5c2df09a58dab51d45f5c922ca/typescript-client/client.ts#L960-L996","documentation":"Thrown after the raw fetch to the job_helpers/download_s3_file endpoint when the response is not ok. Raw fetch is used because the generated OpenAPI client mishandles Blob responses, so the server's error body is read manually and inlined — the message carries the HTTP status plus the API's error detail (missing object, wrong key, denied permission).","triggerScenarios":"Calling the S3 file load helper when the file key does not exist, the workspace/storage is wrong, or the caller lacks permission on the S3 object.","commonSituations":"Stale or mistyped S3 file key; file deleted upstream; wrong workspace; storage misconfiguration; expired presigned access.","solutions":["Check the status code in the message (404 = key not found, 403 = permission) and fix accordingly","Verify the S3Object file key and storage value are correct","Confirm the file exists (e.g. via the S3 file list API) before loading","Ensure the workspace is correct and the token has S3 read access"],"exampleFix":"// before\nconst blob = await loadS3File({ s3: 'data/file.json' })\n// after\ntry {\n  const blob = await loadS3File({ s3: 'data/file.json' })\n} catch (e) {\n  if (String(e.message).includes('404')) console.error('S3 file does not exist')\n  throw e\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  const blob = await loadS3File(s3Object, workspace)\n} catch (e) {\n  if (String(e.message).startsWith('Failed to load S3 file:')) {\n    const status = Number(String(e.message).match(/(\\d{3})/)?.[1])\n    if (status === 404) console.error('S3 key not found')\n    else if (status === 403) console.error('No permission for S3 object')\n  } else throw e\n}","preventionTips":["Verify keys exist via the S3 file list API before loading","Confirm workspace and storage values match where the file was written","Handle presigned/permission expiry in long-running processes"],"tags":["s3","network","http","file"],"backgroundTag":"http-request-failed","analyzedSha":"e474e8803ce2ff5c2df09a58dab51d45f5c922ca","analyzedAt":"2026-09-03T12:38:19.024Z","contentChangedAt":"2026-09-03T12:38:19.024Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}