koala73/worldmonitor · warning · IncompatibleDeliveryError
INCOMPATIBLE_DELIVERY
INCOMPATIBLE_DELIVERY
Error message
Real-time delivery requires High or Critical sensitivity.
What it means
Default message for IncompatibleDeliveryError, thrown by setNotificationConfig() in src/services/notification-channels.ts when POST /api/notification-channels (action=set-notification-config) answers 400 with body.error === 'INCOMPATIBLE_DELIVERY'. The server's cross-field validator rejects digestMode 'realtime' paired with sensitivity 'all' — real-time delivery is only allowed with High or Critical sensitivity (plan forbid-realtime-all-events.md §1f). The dedicated error class lets the settings UI render inline helper text instead of a generic failure; the exact server message overrides this default when present.
Source
Thrown at src/services/notification-channels.ts:463
sensitivity?: Sensitivity;
channels?: ChannelType[];
aiDigestEnabled?: boolean;
digestMode?: DigestMode;
digestHour?: number;
digestTimezone?: string;
countries?: string[];
tickers?: string[];
}, expectedUserId?: string, signal?: AbortSignal): Promise<void> {
const res = await authFetch('/api/notification-channels', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ action: 'set-notification-config', ...args }),
}, expectedUserId, signal);
if (res.ok) return;
let body: { error?: string; message?: string } = {};
try { body = await res.json(); } catch { /* keep default */ }
if (res.status === 400 && body.error === 'INCOMPATIBLE_DELIVERY') {
throw new IncompatibleDeliveryError(
body.message ?? 'Real-time delivery requires High or Critical sensitivity.',
);
}
throw new Error(`set notification config: ${res.status}`);
}
View on GitHub (pinned to eeab0a219f)
Solutions
- Set sensitivity to 'high' or 'critical' in the same call that sets digestMode: 'realtime' — the endpoint applies both atomically.
- Pre-validate the pair client-side before POSTing (mirror the rule) so the user gets feedback before submit.
- Catch IncompatibleDeliveryError specifically (instanceof) and show the server's body.message inline rather than a generic error.
- If migrating legacy configs, upgrade sensitivity in the same payload when switching to realtime.
Example fix
// before
await setNotificationConfig({ variant: 'global', digestMode: 'realtime', sensitivity: 'all' });
// after
const sensitivity = args.digestMode === 'realtime' && args.sensitivity === 'all'
? 'high'
: args.sensitivity;
await setNotificationConfig({ ...args, sensitivity }); Defensive patterns
Strategy: validation
Validate before calling
const REALTIME_SENSITIVITY = new Set(['high', 'critical']);
if (config.digestMode === 'realtime' && !REALTIME_SENSITIVITY.has(config.sensitivity)) {
showInlineError('Real-time delivery requires High or Critical sensitivity.');
return;
}
await setNotificationConfig(config); Type guard
import { IncompatibleDeliveryError } from '@/services/notification-channels';
function isIncompatibleDelivery(err: unknown): err is IncompatibleDeliveryError {
return err instanceof IncompatibleDeliveryError;
} Try / catch
try {
await setNotificationConfig(args);
} catch (err) {
if (isIncompatibleDelivery(err)) {
showInlineHelper(err.message); // render guidance, keep the form open
return;
}
throw err;
} Prevention
- Mirror the (digestMode, sensitivity) compatibility rule client-side so invalid pairs never POST.
- Always send digestMode and sensitivity together in one set-notification-config call — the endpoint validates them atomically.
- Catch IncompatibleDeliveryError by instanceof and render its message inline rather than a toast.
When it happens
Trigger: Calling setNotificationConfig({ digestMode: 'realtime', sensitivity: 'all', ... }) (or leaving sensitivity at 'all' while switching digestMode to 'realtime'); a UI flow that saves digest mode and sensitivity in separate steps can also trip the validator transiently; any API consumer that assumes the (realtime, all) pair is accepted.
Common situations: Users switching from daily digest to real-time without raising sensitivity first; stale clients after the validation rule shipped, still sending the previously-legal combination; form state bugs that reset sensitivity to 'all' silently.
Related errors
- INCOMPATIBLE_DELIVERY
- callbackUrl is required
- callbackUrl is not allowed
- Unknown chokepoint ID: ${invalidCp}
- alertThreshold must be between 0 and 100
AI-assisted analysis of koala73/worldmonitor@eeab0a219f (2026-08-21).
Data as JSON: /api/errors/b03e155fcc875665.
Report an issue: GitHub.