immich-app/immich · error · BadRequestException

May not request original file

Error message

May not request original file

What it means

viewThumbnail serves thumbnail/preview/fullsize renditions of an asset. Requesting size=original through this endpoint is explicitly rejected because original files must be fetched via the dedicated original-file endpoint (viewOriginal), which performs different access checks (e.g. shared-link download permissions).

Solutions

  1. Request size=preview or size=fullsize on the thumbnail endpoint.
  2. To get the original file, call the original endpoint: GET /api/assets/:id/original.
  3. Update outdated client SDK versions that still map 'original' to the thumbnail route.

Example fix

// before
await fetch(`${base}/api/assets/${id}/thumbnail?size=original`);
// after
await fetch(`${base}/api/assets/${id}/original`); // originals use the dedicated endpoint
Defensive patterns

Strategy: validation

Validate before calling

if (size === 'original') throw new Error('Use /original endpoint for original files');
const url = size === 'original' ? `/api/assets/${id}/original` : `/api/assets/${id}/thumbnail?size=${size}`;

Type guard

const isThumbnailSize = (s: string): s is 'preview' | 'fullsize' => s === 'preview' || s === 'fullsize';

Prevention

When it happens

Trigger: GET /api/assets/:id/thumbnail?size=original (AssetMediaSize.ORIGINAL) — passing AssetMediaSize.Original in the dto to viewThumbnail.

Common situations: Client code copying URL templates and reusing size=original for all sizes; older API clients built before the original/thumbnail endpoints were split; hand-written scripts generating thumbnail URLs.

Understand the failure class

Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.

Related errors


AI-assisted analysis of immich-app/immich@e55ac299a4 (2026-09-15). Data as JSON: /api/errors/41af444ecec10dd0. Report an issue: GitHub.

Appendix: source

Thrown at server/src/services/asset-media.service.ts:259

    const path = editedPath ?? originalPath!;

    return new ImmichFileResponse({
      path,
      fileName: getFileNameWithoutExtension(originalFileName) + getFilenameExtension(path),
      contentType: mimeTypes.lookup(path),
      cacheControl: CacheControl.PrivateWithCache,
    });
  }

  async viewThumbnail(
    auth: AuthDto,
    id: string,
    dto: AssetMediaOptionsDto,
  ): Promise<ImmichFileResponse | AssetMediaRedirectResponse> {
    await this.requireAccess({ auth, permission: Permission.AssetView, ids: [id] });

    if (dto.size === AssetMediaSize.Original) {
      throw new BadRequestException('May not request original file');
    }

    if (auth.sharedLink) {
      dto.edited = true;
    }

    const size = (dto.size ?? AssetMediaSize.THUMBNAIL) as unknown as AssetFileType;
    const { originalPath, originalFileName, path } = await this.assetRepository.getForThumbnail(
      id,
      size,
      dto.edited ?? false,
    );

    if (size === AssetFileType.FullSize && mimeTypes.isWebSupportedImage(originalPath) && !dto.edited) {
      // use original file for web supported images
      return { targetSize: 'original' };
    }

View on GitHub (pinned to e55ac299a4)