toeverything/AFFiNE · warning · BadRequest

bad_request

bad_request

Error message

Invalid origin: ${origin}, referer: ${referer}

What it means

Thrown by TelemetryController.collectOptions() (the CORS preflight handler for OPTIONS /api/telemetry/collect) when TelemetryService.isOriginAllowed() returns false for the request's Origin/Referer headers. The service allows the request only if the origin (or the referer's origin) is in the configured allowlist, or if both headers are absent. This guards the preflight response from being issued for disallowed cross-origin callers.

Source

Thrown at packages/backend/server/src/core/telemetry/controller.ts:25

  type CurrentUser as CurrentUserType,
  Public,
} from '../auth';
import { TelemetryService } from './service';
import { TelemetryAck, type TelemetryBatch } from './types';

@Public()
@UseNamedGuard('version')
@Throttle('default')
@Controller('/api/telemetry')
export class TelemetryController {
  constructor(private readonly telemetry: TelemetryService) {}

  @Options('/collect')
  collectOptions(@Req() req: Request, @Res() res: Response) {
    const origin = req.headers.origin;
    const referer = req.headers.referer;
    if (!this.telemetry.isOriginAllowed(origin, referer)) {
      throw new BadRequest(`Invalid origin: ${origin}, referer: ${referer}`);
    }

    return res
      .status(200)
      .header({
        ...this.telemetry.getCorsHeaders(origin),
        'Access-Control-Allow-Methods': 'POST, OPTIONS',
        'Access-Control-Allow-Headers': 'Content-Type, x-affine-version',
      })
      .send();
  }

  @Post('/collect')
  async collect(
    @Req() req: Request,
    @Res({ passthrough: true }) res: Response,
    @Body() batch: TelemetryBatch,
    @CurrentUser() user?: CurrentUserType

View on GitHub (pinned to 26c515e050)

Solutions

  1. Add the front-end origin to the server's telemetry origin allowlist in config.
  2. If behind a proxy, ensure the Origin header is forwarded unchanged to the backend.
  3. Verify the deployment URL matches an entry in the configured allowed origins exactly (scheme + host + port).

Example fix

// config — add the deployed front-end origin
telemetry:
  allowedOrigins:
    - https://app.example.com
    - https://staging.example.com
Defensive patterns

Strategy: validation

Validate before calling

// Client-side: only send telemetry when origin is allowlisted
if (!ALLOWED_ORIGINS.includes(window.location.origin)) return;
fetch('/api/telemetry/collect', { method: 'OPTIONS', headers: { Origin: window.location.origin } });

Type guard

function isOriginAllowed(origin: string, allowlist: string[]): boolean {
  return allowlist.includes(origin);
}

Try / catch

try {
  await fetch('/api/telemetry/collect', { method: 'OPTIONS' });
} catch (e) {
  if (e?.code === 'bad_request') { disableTelemetry(); return; }
  throw e;
}

Prevention

When it happens

Trigger: Browser sends an OPTIONS preflight to /api/telemetry/collect with an Origin header whose value is not in telemetry.allowedOrigins, and the Referer origin also is not allowlisted.

Common situations: Deploying AFFiNE under a new domain without updating the telemetry allowed origins config; front-end loaded from a CDN/staging domain not in the allowlist; reverse proxy stripping or rewriting the Origin header.

Related errors


AI-assisted analysis of toeverything/AFFiNE@26c515e050 (2026-08-12). Data as JSON: /api/errors/c43051bb0b956960. Report an issue: GitHub.