different-ai/openwork · error

Failed to verify domain

Error message

Failed to verify domain (${response.status}).

What it means

Thrown by handleVerifyDomain when POST /v1/sso/verify-domain returns a status other than 204 or generic non-ok. The server checks the org's DNS TXT record for the issued domainVerificationToken; failure here is usually that the DNS record has not propagated or does not match. Success clears the token and reloads the SSO config.

Solutions

  1. Confirm the TXT record exists via `dig TXT yourdomain.com +short` and exactly matches the issued domainVerificationToken (watch extra quotes).
  2. Wait for DNS propagation (TTL up to hours) and retry — 409/422 here is almost always propagation lag.
  3. If the token expired (404), call handleRequestDomainToken again to re-issue, publish, then verify.
  4. Ensure a token was requested in this session (domainVerificationToken !== null) before verifying.
  5. Handle ReauthRequiredError via re-authentication and check admin permissions for 403.

Example fix

// before: immediate verify after showing token
setDomainVerificationToken(token);
await handleVerifyDomain();
// after: guide the user through DNS first
setDomainVerificationToken(token);
setError(`Add TXT record _openwork to your domain with value ${token}, then verify after it propagates.`);
// verification stays user-triggered; on 409/422 show retry-later message
Defensive patterns

Strategy: retry

Validate before calling

if (!domainVerificationToken) { setError("Request a domain verification token first."); return; }
// before verifying, optionally pre-check DNS from the client (informational only):
// const txt = await fetch(`https://dns.google/resolve?name=_openwork.${domain}&type=TXT`)

Type guard

function isReauthRequiredError(e: unknown): e is ReauthRequiredError {
  return e instanceof ReauthRequiredError;
}

Try / catch

try {
  await verifyDomain();
} catch (err) {
  if (isReauthRequiredError(err)) { promptSignIn(); return; }
  if (/\b(409|422)\b/.test(err.message)) {
    setError("TXT record not found yet — wait for DNS propagation and try again.");
    return; // retryable
  }
  if (/\b404\b/.test(err.message)) { setError("Verification request expired; request a new token."); return; }
  setError(err.message);
}

Prevention

When it happens

Trigger: POST /v1/sso/verify-domain with org-scoped headers returns 400 (no pending domainVerificationToken was requested), 401 (expired session), 403 (not org admin / reauth challenge), 404 (verification request expired), 409/422 (TXT record not found or mismatched — DNS not propagated), 429, or 5xx. 12s timeout.

Common situations: Verifying minutes after adding the TXT record while DNS TTL hasn't elapsed; TXT record added to the wrong domain/subdomain or with wrong quoting; token expired and needs re-issue; the org never requested a token in this session.

Understand the failure class

Background: 'Something went wrong' / 'Request failed (500)' / 'HTTP error! status: 404' — what failed HTTP requests actually mean and how to find the real cause — this error's family across 28 libraries.

Related errors


AI-assisted analysis of different-ai/openwork@2b7df46e8a (2026-09-01). Data as JSON: /api/errors/00f0f1e4ca34ffd5. Report an issue: GitHub.

Appendix: source

Thrown at ee/apps/den-web/app/(den)/dashboard/_components/sso-screen.tsx:287

    } catch (nextError) {
      setError(nextError instanceof Error ? nextError.message : "Failed to request domain verification.");
    }
  }

  async function handleVerifyDomain() {
    if (!access.canManageSso) {
      setError("Only workspace owners and super-admins can verify SSO domains.");
      return;
    }
    if (!orgId || !connection) return;
    setError(null);
    try {
      await runReauthableAction("verify-sso-domain", async () => {
        setVerifyingDomain(true);
        try {
          const { response, payload } = await requestJson("/v1/sso/verify-domain", { method: "POST", headers: getOrgScopedHeaders(), body: JSON.stringify({}) }, 12000);
          if (response.status !== 204 && !response.ok) {
            throw getRequestError(payload, response, `Failed to verify domain (${response.status}).`);
          }
          setDomainVerificationToken(null);
          await loadSsoConfig();
        } finally {
          setVerifyingDomain(false);
        }
      });
    } catch (nextError) {
      setError(nextError instanceof Error ? nextError.message : "Failed to verify the SSO domain.");
    }
  }

  function handleCancelEdit() {
    syncFormFromConnection(connection);
    setEditing(false);
  }

  const formReadOnly = !access.canManageSso;

View on GitHub (pinned to 2b7df46e8a)