koala73/worldmonitor · warning · ApiError
Service temporarily unavailable
Error message
Service temporarily unavailable
What it means
Same revalidation path as listWebhooks but in registerWebhook: when the gateway reports USER_API_KEY_GATEWAY_VALIDATION_ERROR, the server revalidates the header credential itself via validateUserApiKey. If that call throws (validation backend unavailable), the handler returns HTTP 503 'Service temporarily unavailable' rather than the internal failure.
Solutions
- Retry the registration after a short backoff — the 503 indicates a transient server-side condition
- Check service health/status before repeated retries
- Ensure the API key header is actually attached so validation behaves predictably
- If the outage persists, delay webhook registration and re-run your setup script later
Example fix
// before
await client.registerWebhook({ callbackUrl }); // 503 during auth outage
// after
await backoff(() => client.registerWebhook({ callbackUrl }), {
retries: 3,
retryOn: (e) => e.status === 503,
}); Defensive patterns
Strategy: retry
Try / catch
try {
return await client.registerWebhook(req);
} catch (e) {
if (e.status === 503) {
await sleep(backoff(attempt++));
return registerWithRetry(req);
}
throw e;
} Prevention
- Treat 503 on registration as transient; retry with backoff
- Keep registration scripts idempotent so retries are safe
- Alert on repeated 503s as a backend health signal
When it happens
Trigger: POSTing to register-webhook with a user API key during an outage of the key-validation service; validateUserApiKey throws inside the catch and the 503 is raised.
Common situations: Auth backend outage or timeout at peak load; network issue between the API server and the credential store; transient errors right after key rotation while caches propagate.
Related errors
- Service temporarily unavailable
- Webhook registration could not be confirmed
- Webhook index could not be read or cleaned
- Revoke service is temporarily unavailable. Try again in a mo
- Invalid scorecard bloc selection.
AI-assisted analysis of koala73/worldmonitor@7d06c8633d (2026-09-15).
Data as JSON: /api/errors/65b7d8d46f0c2645.
Report an issue: GitHub.
Appendix: source
Thrown at server/worldmonitor/shipping/v2/register-webhook.ts:52
): Promise<RegisterWebhookResponse> {
// Webhooks are per-tenant keyed on callerFingerprint(), which hashes the
// API key. Without forceKey, a Clerk-authenticated pro caller reaches this
// handler with no API key, callerFingerprint() falls back to 'anon', and
// every such caller collapses into a shared 'anon' owner bucket — letting
// one Clerk-session holder enumerate/overwrite other tenants' webhooks.
// Matches the legacy `api/v2/shipping/webhooks/[subscriberId]{,/[action]}.ts`
// gate and the documented "X-WorldMonitor-Key required" contract in
// docs/api-shipping-v2.mdx.
const apiKeyResult = (await validateApiKey(ctx.request, { forceKey: true })) as {
valid: boolean; required: boolean; error?: string; credential?: string;
};
if (apiKeyResult.error === USER_API_KEY_GATEWAY_VALIDATION_ERROR) {
const credential = getHeaderApiKey(ctx.request) as string;
let userKey;
try {
userKey = credential ? await validateUserApiKey(credential) : null;
} catch {
throw new ApiError(503, 'Service temporarily unavailable', '');
}
if (!userKey) throw new ApiError(401, 'Invalid API key', '');
// Revalidate the credential rather than trusting a caller-supplied user ID.
apiKeyResult.valid = true;
apiKeyResult.credential = credential;
}
if (apiKeyResult.required && !apiKeyResult.valid) {
throw new ApiError(401, apiKeyResult.error ?? 'API key required', '');
}
await requirePremiumRpcAccess(ctx.request, ApiError, 'PRO subscription required');
const callbackUrl = (req.callbackUrl ?? '').trim();
if (!callbackUrl) {
throw new ValidationError([{ field: 'callbackUrl', description: 'callbackUrl is required' }]);
}
try {View on GitHub (pinned to 7d06c8633d)