immich-app/immich · error · NotAcceptableException

The route was requested as , but only returns text/html

Error message

The route ${request.path} was requested as ${request.header('accept')}, but only returns text/html

What it means

Raised by the SSR middleware in ApiService when a non-/api GET/HEAD request negotiates a response type other than text/html (request.accepts('text/html') is falsy). The server-rendered web client can only serve HTML, so clients asking for JSON or another content type on a web route get this error instead of the SPA index. The inputs at fault are the request's path and Accept header.

Solutions

  1. Send Accept: text/html (or */*) when fetching web routes
  2. Use the /api-prefixed REST endpoints for machine-readable responses
Defensive patterns

Strategy: validation

When it happens

Trigger: Thrown at server/src/services/api.service.ts:61 when the library encounters an invalid state.

Common situations: See trigger scenarios.


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

Appendix: source

Thrown at server/src/services/api.service.ts:61

    try {
      index = readFileSync(resourcePaths.web.indexHtml).toString();
    } catch {
      this.logger.warn(`Unable to open ${resourcePaths.web.indexHtml}, skipping SSR.`);
    }

    return async (request: Request, res: Response, next: NextFunction) => {
      const method = request.method.toLowerCase();
      if (
        request.url.startsWith('/api') ||
        (method !== 'get' && method !== 'head') ||
        excludePaths.some((item) => request.url.startsWith(item))
      ) {
        return next();
      }

      const responseType = request.accepts('text/html');
      if (!responseType) {
        throw new NotAcceptableException(
          `The route ${request.path} was requested as ${request.header('accept')}, but only returns text/html`,
        );
      }

      let status = 200;
      let html = index;

      const defaultDomain = request.host ? `${request.protocol}://${request.host}` : undefined;

      let meta: OpenGraphTags | null = null;

      const shareKey = request.url.match(/^\/share\/(.+)$/);
      if (shareKey) {
        try {
          const key = shareKey[1];
          const auth = await this.authService.validateSharedLinkKey(key);
          meta = await this.sharedLinkService.getMetadataTags(auth, defaultDomain);
        } catch {

View on GitHub (pinned to e55ac299a4)