immich-app/immich · error · Error

await response.text()

Error message

await response.text()

What it means

uploadFile performs the asset-media PUT via fetch-like ofetch; when the Immich server responds with any status other than 200/201, the raw body text is read and thrown as the Error message. The developer-facing message is therefore whatever the server returned (e.g. an HTML error page, a JSON problem body, or an empty string), not a structured error.

Solutions

  1. Read the thrown message: it contains the server's response body, which usually names the real cause (401 => fix IMMICH_API_KEY, 413 => raise client_max_body_size).
  2. For 413 behind nginx, set client_max_body_size (e.g. 50000M) and restart the proxy.
  3. Verify the server URL and API key with `immich server-info`.
  4. Retry large uploads on a stable connection; check Immich server logs for the corresponding request error.
Defensive patterns

Strategy: try-catch

Validate before calling

const info = await getServerInfo(); // verifies API key + URL before big uploads
if (!info) throw new Error('check IMMICH_INSTANCE_URL / IMMICH_API_KEY');

Try / catch

try {
  await uploadFile(filepath, stats, options);
} catch (err) {
  const msg = String(err);
  if (msg.includes('401') || msg.toLowerCase().includes('unauthorized')) fixApiKeyAndRetry();
  else if (/413|too large/i.test(msg)) console.error('raise client_max_body_size on the reverse proxy');
  else throw err;
}

Prevention

When it happens

Trigger: The server returns 400 (bad upload payload/size limits), 401/403 (bad or missing API key), 413 (file too large for the reverse proxy, e.g. nginx client_max_body_size), or 5xx (server crash/storage full) during `immich upload`.

Common situations: Expired or wrong IMMICH_API_KEY, reverse proxy (nginx/Traefik/Cloudflare) rejecting large video files with 413, Immich server restarting mid-upload, or uploading to the wrong base URL hitting an auth/HTML page.

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 immich-app/immich@e55ac299a4 (2026-09-15). Data as JSON: /api/errors/139e8077e726e1db. Report an issue: GitHub.

Appendix: source

Thrown at packages/cli/src/commands/asset.ts:441

    try {
      const stats = await stat(sidecarPath);
      const sidecarData = new UploadFile(sidecarPath, stats.size);
      formData.append('sidecarData', sidecarData);
    } catch {
      // noop
    }
  }

  const response = await fetch(`${baseUrl}/assets`, {
    method: 'post',
    redirect: 'error',
    headers: headers as Record<string, string>,
    body: formData,
    // eslint-disable-next-line unicorn/no-null
    window: null,
  });
  if (response.status !== 200 && response.status !== 201) {
    throw new Error(await response.text());
  }

  return response.json() as Promise<AssetMediaResponseDto>;
};

export const findSidecar = (filepath: string): string | undefined => {
  const assetPath = path.parse(filepath);
  const noExtension = path.join(assetPath.dir, assetPath.name);

  // XMP sidecars can come in two filename formats. For a photo named photo.ext, the filenames are photo.ext.xmp and photo.xmp
  for (const sidecarPath of [`${noExtension}.xmp`, `${filepath}.xmp`]) {
    if (existsSync(sidecarPath)) {
      return sidecarPath;
    }
  }
};

export const deleteFiles = async (uploaded: Asset[], duplicates: Asset[], options: UploadOptionsDto): Promise<void> => {

View on GitHub (pinned to e55ac299a4)