koala73/worldmonitor · error
EMAIL_OWNERSHIP_REQUIRED
EMAIL_OWNERSHIP_REQUIRED
Error message
Verify your account email, then try again.
What it means
setEmailChannel() throws this with code EMAIL_OWNERSHIP_REQUIRED when the notification-channel API responds with that exact error, meaning the backend requires the account's email to be verified before an email notification channel can be attached. It is a policy check, not a transport failure.
Solutions
- Verify the account email: request/resend the Clerk verification email and click the verification link, then retry setEmailChannel()
- Check verification state via Clerk (user.emailAddresses[].verification.status === 'verified') before offering the email channel
- If the email was changed recently, complete the new address's verification before attaching notifications
Example fix
// before
await setEmailChannel(email);
// after
const user = useUser();
if (user.emailAddresses.some(a => a.verification?.status !== 'verified')) {
showBanner('Verify your email first.');
return;
}
await setEmailChannel(email); Defensive patterns
Strategy: validation
Validate before calling
const verified = user?.emailAddresses?.some(a => a.verification?.status === 'verified'); if (!verified) { showVerifyEmailBanner(); return; } Type guard
const isEmailVerified = (u: ClerkUser | null): boolean => !!u?.emailAddresses?.some(a => a.verification?.status === 'verified');
Try / catch
try { await setEmailChannel(email); } catch (e) { if (e.message.includes('Verify your account email')) { redirectToEmailVerification(); } else throw e; } Prevention
- Require verified email before showing email-channel UI
- Resend verification links from settings
- Re-check verification after any account email change
When it happens
Trigger: Calling setEmailChannel(email) (directly or via confirmBrief/attach) for an email address that is not verified on the account, and the API replying {error: 'EMAIL_OWNERSHIP_REQUIRED'}.
Common situations: Newly signed-up users whose Clerk email is unverified; users who changed their account email but haven't clicked the verification link; attaching a brief's email notifications right after signup.
Understand the failure class
Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.
Related errors
- Could not connect email. Please try again.
- INCOMPATIBLE_DELIVERY
- COUNTRIES_LIMIT_EXCEEDED
- Invalid email address
- emailCheck.reason
AI-assisted analysis of koala73/worldmonitor@7d06c8633d (2026-09-15).
Data as JSON: /api/errors/d57b04371f647f71.
Report an issue: GitHub.
Appendix: source
Thrown at src/services/notification-channels.ts:266
*
* `startSlackOAuth` / `startDiscordOAuth` keep the read semantics — they persist
* nothing the user would miss, and abandoning a popup handoff on teardown is
* correct.
*/
export async function setEmailChannel(
email: 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-channel', channelType: 'email', email }),
}, expectedUserId, signal);
if (!res.ok) {
const failure = await res.json().catch(() => null);
if (failure?.error === 'EMAIL_OWNERSHIP_REQUIRED') {
throw new Error('Verify your account email, then try again.');
}
throw new Error('Could not connect email. Please try again.');
}
}
export async function setSlackChannel(
webhookEnvelope: string,
signal?: AbortSignal,
): Promise<void> {
const res = await authFetch('/api/notification-channels', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ action: 'set-channel', channelType: 'slack', webhookEnvelope }),
}, undefined, signal);
if (!res.ok) throw new Error(`set slack channel: ${res.status}`);
}
export async function setWebhookChannel(View on GitHub (pinned to 7d06c8633d)