{"record":{"id":"687ea522452bddde","repo":"gildas-lormeau/SingleFile","slug":"response-statustext-error-response-status","errorCode":null,"errorMessage":"response.statusText || \"Error \" + response.status","messagePattern":"response\\.statusText \\|\\| \"Error \" \\+ response\\.status","errorType":"http","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/lib/s3/s3.js","lineNumber":92,"sourceCode":"\t\t\t\t\t\t\t\treturn this.upload(path, blob, options);\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\treturn response;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\toptions.filenameConflictAction = CONFLICT_ACTION_UNIQUIFY;\n\t\t\t\t\t\t\treturn this.upload(path, blob, options);\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (filenameConflictAction == CONFLICT_ACTION_UNIQUIFY) {\n\t\t\t\t\t\tconst { filenameWithoutExtension, extension, indexFilename } = splitFilename(path);\n\t\t\t\t\t\toptions.indexFilename = indexFilename + 1;\n\t\t\t\t\t\tpath = getFilename(filenameWithoutExtension, options.indexFilename, extension);\n\t\t\t\t\t\treturn this.upload(path, blob, options);\n\t\t\t\t\t}\n\t\t\t\t} else if (response.status == 404) {\n\t\t\t\t\tblob = new Uint8Array(await blob.arrayBuffer());\n\t\t\t\t\treturn this.api.putObject({ path }, { body: await getUint8Array(blob) });\n\t\t\t\t} else {\n\t\t\t\t\tthrow new Error(response.statusText || \"Error \" + response.status);\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tif (error.name != ABORT_ERROR_NAME) {\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t}\n\t}\n\n\tabort() {\n\t\tif (this.controller) {\n\t\t\tthis.controller.abort();\n\t\t}\n\t}\n}\n\nasync function getUint8Array(blob) {\n\treturn new Uint8Array(await new Response(blob).arrayBuffer());","sourceCodeStart":74,"sourceCodeEnd":110,"githubUrl":"https://github.com/gildas-lormeau/SingleFile/blob/517fb7c5cf2096d89933b747e862d8ecf616a9f9/src/lib/s3/s3.js#L74-L110","documentation":"upload() in the S3 client retries 5xx/abortable failures and special-cases 404 (falling back to putObject), but for any other failing response it throws an Error using response.statusText, falling back to \"Error <status>\" when statusText is empty (common on HTTP/2, where statusText is blank).","triggerScenarios":"PUT of a blob to S3-compatible storage returning 400 (bad checksum/part size), 401/403 (invalid credentials or missing permission), 409, or 412 — i.e., a failure status that is neither retried nor the 404 path.","commonSituations":"Wrong S3 credentials or expired presigned URL; bucket/endpoint misconfiguration; statusText empty on HTTP/2 giving the opaque \"Error 403\" style message; server rejects the blob size or content type.","solutions":["Capture response.status in the error (statusText is often empty) — throw `${response.status}: ${response.statusText}` so the code is visible","Validate S3 credentials, bucket name and endpoint configuration","For 404-triggered putObject fallbacks, check the putObject call's own errors (path/permissions)","Retry idempotent uploads with backoff on 5xx and check network/proxy stability"],"exampleFix":"// before\nthrow new Error(response.statusText || \"Error \" + response.status);\n// after\nthrow new Error(`S3 upload failed: ${response.status} ${response.statusText || \"\"}`.trim());","handlingStrategy":"retry","validationCode":"if (!s3Config.accessKeyId || !s3Config.secretAccessKey) throw new Error(\"Missing S3 credentials\");\nconst head = await fetch(endpoint, { method: \"HEAD\" });\nif (head.status === 403) throw new Error(\"S3 credentials rejected — check keys/endpoint\");","typeGuard":"function isRetryableStatus(status) { return status === 429 || status >= 500; }","tryCatchPattern":"try {\n  await s3.upload(path, blob, options);\n} catch (e) {\n  const status = parseStatus(e.message); // error text carries status\n  if (isRetryableStatus(status)) await retryWithBackoff(() => s3.upload(path, blob, options), 3);\n  else if (status === 401 || status === 403) throw new Error(`S3 auth failed (${status}); check credentials`);\n  else throw e;\n}","preventionTips":["Log both status and statusText (statusText is empty on HTTP/2)","Verify credentials, bucket and endpoint config before upload","Use idempotent retry with backoff for 5xx","Check content-length/part-size constraints for large blobs"],"tags":["s3","http","upload"],"backgroundTag":"http-error-response","analyzedSha":"517fb7c5cf2096d89933b747e862d8ecf616a9f9","analyzedAt":"2026-09-01T10:05:25.770Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}