gildas-lormeau/SingleFile · error · Error
response.statusText || "Error " + response.status
Error message
response.statusText || "Error " + response.status
What it means
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).
Source
Thrown at src/lib/s3/s3.js:92
return this.upload(path, blob, options);
} else {
return response;
}
} else {
options.filenameConflictAction = CONFLICT_ACTION_UNIQUIFY;
return this.upload(path, blob, options);
}
} else if (filenameConflictAction == CONFLICT_ACTION_UNIQUIFY) {
const { filenameWithoutExtension, extension, indexFilename } = splitFilename(path);
options.indexFilename = indexFilename + 1;
path = getFilename(filenameWithoutExtension, options.indexFilename, extension);
return this.upload(path, blob, options);
}
} else if (response.status == 404) {
blob = new Uint8Array(await blob.arrayBuffer());
return this.api.putObject({ path }, { body: await getUint8Array(blob) });
} else {
throw new Error(response.statusText || "Error " + response.status);
}
}
} catch (error) {
if (error.name != ABORT_ERROR_NAME) {
throw error;
}
}
}
abort() {
if (this.controller) {
this.controller.abort();
}
}
}
async function getUint8Array(blob) {
return new Uint8Array(await new Response(blob).arrayBuffer());View on GitHub (pinned to 517fb7c5cf)
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
Example fix
// before
throw new Error(response.statusText || "Error " + response.status);
// after
throw new Error(`S3 upload failed: ${response.status} ${response.statusText || ""}`.trim()); Defensive patterns
Strategy: retry
Validate before calling
if (!s3Config.accessKeyId || !s3Config.secretAccessKey) throw new Error("Missing S3 credentials");
const head = await fetch(endpoint, { method: "HEAD" });
if (head.status === 403) throw new Error("S3 credentials rejected — check keys/endpoint"); Type guard
function isRetryableStatus(status) { return status === 429 || status >= 500; } Try / catch
try {
await s3.upload(path, blob, options);
} catch (e) {
const status = parseStatus(e.message); // error text carries status
if (isRetryableStatus(status)) await retryWithBackoff(() => s3.upload(path, blob, options), 3);
else if (status === 401 || status === 403) throw new Error(`S3 auth failed (${status}); check credentials`);
else throw e;
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.
Related errors
- error.message + " (S3)"
- unknown_error (" + httpFinishResponse.status + ")
- await response.text()
- Error " + response.status
- error.message + " (GitHub)"
AI-assisted analysis of gildas-lormeau/SingleFile@517fb7c5cf (2026-09-01).
Data as JSON: /api/errors/687ea522452bddde.
Report an issue: GitHub.