koala73/worldmonitor · warning · ApiError
Too many recent submissions for this email; try again later.
Error message
Too many recent submissions for this email; try again later.
What it means
The Convex backend answered 429, meaning this email has submitted contact requests too recently. The handler surfaces it as an explicit 429 ApiError with a retry-later message — this is a deliberate rate-limit signal, not an outage.
Solutions
- Wait for the rate-limit window to elapse and retry with the same email
- Back off client-side: disable submit after first click and honor the 429 with a countdown UI
- Use a different email address if the request is legitimately for another contact
- Raise or tune the rate-limit window in the Convex submitContact action if it is too strict for legitimate traffic
Example fix
// before
if (response.status === 429) {
throw new ApiError(429, 'Too many recent submissions for this email; try again later.', '');
}
// after
if (response.status === 429) {
const retryAfter = response.headers.get('retry-after');
throw new ApiError(429, 'Too many recent submissions for this email; try again later.', '', retryAfter ? { 'retry-after': retryAfter } : undefined);
} Defensive patterns
Strategy: retry
Validate before calling
const lastSubmitAt = Number(localStorage.getItem('contact:lastSubmitAt') ?? 0);
if (Date.now() - lastSubmitAt < RATE_WINDOW_MS) show('Please wait before submitting again.'); Type guard
null
Try / catch
try {
await submitContact(form);
} catch (e) {
if (e instanceof ApiError && e.status === 429) {
showCountdownAndRetryLater(e.message);
} else throw e;
} Prevention
- Disable the submit button after the first click to avoid duplicates
- Honor Retry-After / show a countdown UI on 429
- Never reuse one fixture email in automated tests; vary addresses
When it happens
Trigger: Posting /submit-contact repeatedly with the same email within the backend's rate-limit window, causing the Convex action to respond with HTTP 429.
Common situations: A user double-clicking the submit button, automated scripts or retries hammering the endpoint, a shared corporate mail gateway reusing one address, or integration tests reusing a fixture email.
Related errors
- Scenario queue is at capacity, please try again later
- Imagery search rate limited
- relay returned ${resp.status}
- Exa ${endpoint.slice(1)} failed HTTP ${response.status}: ${d
- Revoke failed (HTTP ${resp.status}).
AI-assisted analysis of koala73/worldmonitor@7d06c8633d (2026-09-15).
Data as JSON: /api/errors/c95da7df795acbad.
Report an issue: GitHub.
Appendix: source
Thrown at server/worldmonitor/leads/v1/submit-contact.ts:180
'Content-Type': 'application/json',
'User-Agent': 'worldmonitor-leads/1.0',
'x-convex-shared-secret': secret,
},
body: JSON.stringify({
name: safeName,
email: email.trim(),
organization: safeOrg,
phone: safePhone,
message: safeMsg,
source: safeSource,
}),
signal: AbortSignal.timeout(10_000),
});
} catch {
throw new ApiError(503, 'Service unavailable', '');
}
if (response.status === 429) {
throw new ApiError(429, 'Too many recent submissions for this email; try again later.', '');
}
if (response.status === 422) {
throw new ApiError(422, 'Please use a corporate email address.', '');
}
if (!response.ok) {
throw new ApiError(503, 'Service unavailable', '');
}
const result: unknown = await response.json().catch(() => null);
if (!result || typeof result !== 'object' || !('status' in result) || result.status !== 'sent') {
throw new ApiError(503, 'Service unavailable', '');
}
const emailSent = await sendNotificationEmail(
safeName,
email.trim(),
safeOrg,
safePhone,
safeMsg,View on GitHub (pinned to 7d06c8633d)