toeverything/AFFiNE · warning · BadRequest

bad_request

bad_request

Error message

Mail delivery analytics window must be 24 or 168 hours.

What it means

normalizeMailWindow validates the hours argument of the admin mail-delivery analytics query: only undefined (defaults to 24), 24, or 168 are accepted; anything else throws BadRequest. The value also selects the bucket: 24h→hourly buckets, 168h→daily buckets.

Solutions

  1. Pass exactly 24, 168, or omit the argument
  2. Fix the client to offer only the two supported windows (24h / 7d) instead of a free-form range picker
  3. Validate/coerce the input to a number before sending the GraphQL request
  4. If you need other windows, that requires a backend change — do not try to work around per-request

Example fix

// before
adminMailDeliveries({ input: { hours: windowDays * 24 } });

// after
const hours = windowDays === 7 ? 168 : 24; // only 24 and 168 are valid
adminMailDeliveries({ input: { hours } });
Defensive patterns

Strategy: validation

Validate before calling

const hours = input?.hours;
if (hours !== undefined && hours !== 24 && hours !== 168) {
  throw new Error('hours must be 24, 168, or omitted');
}
await adminMailDeliveries({ input: { hours } });

Type guard

function isInvalidMailWindow(e: unknown): boolean {
  const err = e as { extensions?: { code?: string }; message?: string };
  return err.extensions?.code === 'bad_request' && /24 or 168/.test(err.message ?? '');
}

Try / catch

try {
  const stats = await adminMailDeliveries({ input });
} catch (e) {
  if (isInvalidMailWindow(e)) return loadDefault24h(); // fall back to the default window
  throw e;
}

Prevention

When it happens

Trigger: Querying adminMailDeliveries with hours=48, 12, or 720; a client sending a string like '24' or an interval like '7d' where a number is required; a UI slider that allows arbitrary day ranges instead of the two fixed windows.

Common situations: Custom admin dashboards built on the GraphQL API assuming any window is supported; refactors that started computing hours client-side (e.g. diff between two dates) instead of using the two presets.

Understand the failure class

Background: "Invalid query parameter" / "Failed to parse value of ...": fixing bad query string parameters across APIs — this error's family across 36 libraries.

Related errors


AI-assisted analysis of toeverything/AFFiNE@b4c8548c09 (2026-08-18). Data as JSON: /api/errors/db9a9cb65b3d66cf. Report an issue: GitHub.

Appendix: source

Thrown at packages/backend/server/src/core/mail/resolver.ts:150

}

function startOfUtcDay(value: Date) {
  return new Date(
    Date.UTC(value.getUTCFullYear(), value.getUTCMonth(), value.getUTCDate())
  );
}

function addUtcHours(value: Date, hours: number) {
  return new Date(value.getTime() + hours * 60 * 60 * 1000);
}

function addUtcDays(value: Date, days: number) {
  return new Date(value.getTime() + days * 24 * 60 * 60 * 1000);
}

function normalizeMailWindow(hours: number | undefined) {
  if (hours !== undefined && hours !== 24 && hours !== 24 * 7) {
    throw new BadRequest(
      'Mail delivery analytics window must be 24 or 168 hours.'
    );
  }
  const requestedHours = hours ?? 24;
  const bucket = requestedHours > 24 ? ('day' as const) : ('hour' as const);
  const now = new Date();
  const to =
    bucket === 'hour'
      ? addUtcHours(startOfUtcHour(now), 1)
      : addUtcDays(startOfUtcDay(now), 1);
  const effectiveSize =
    bucket === 'hour' ? requestedHours : requestedHours / 24;
  const from =
    bucket === 'hour'
      ? addUtcHours(to, -effectiveSize)
      : addUtcDays(to, -effectiveSize);

  return {

View on GitHub (pinned to b4c8548c09)