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

  1. Capture response.status in the error (statusText is often empty) — throw `${response.status}: ${response.statusText}` so the code is visible
  2. Validate S3 credentials, bucket name and endpoint configuration
  3. For 404-triggered putObject fallbacks, check the putObject call's own errors (path/permissions)
  4. 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

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


AI-assisted analysis of gildas-lormeau/SingleFile@517fb7c5cf (2026-09-01). Data as JSON: /api/errors/687ea522452bddde. Report an issue: GitHub.