toeverything/AFFiNE · error · NetworkError
network_error
network_error
Error message
Captcha verification temporarily unavailable
What it means
Thrown when the outbound fetch to Cloudflare Turnstile's siteverify endpoint fails outright — DNS failure, TCP/TLS error, or the 5-second AbortSignal.timeout firing. The captcha service treats any inability to reach Turnstile as a temporary network condition, records the 'unavailable' metric, and raises NetworkError so callers can retry rather than blame the user.
Source
Thrown at packages/backend/server/src/plugins/captcha/service.ts:80
formData.append('secret', this.captcha.turnstile.secret);
formData.append('response', token);
formData.append('remoteip', ip);
formData.append('idempotency_key', randomUUID());
const url = 'https://challenges.cloudflare.com/turnstile/v0/siteverify';
let result: Response;
try {
result = await fetch(url, {
body: formData,
method: 'POST',
signal: AbortSignal.timeout(5000),
});
} catch {
metrics.auth.counter('captcha_verification').add(1, {
provider: 'turnstile',
result: 'unavailable',
});
throw new NetworkError('Captcha verification temporarily unavailable');
}
if (!result.ok) {
metrics.auth.counter('captcha_verification').add(1, {
provider: 'turnstile',
result: 'unavailable',
});
throw new NetworkError('Captcha verification temporarily unavailable');
}
let parsed: z.SafeParseReturnType<
unknown,
z.infer<typeof turnstileResponse>
>;
try {
parsed = turnstileResponse.safeParse(await result.json());
} catch {
parsed = turnstileResponse.safeParse(null);
}
if (!parsed.success) {View on GitHub (pinned to b4c8548c09)
Solutions
- Verify outbound HTTPS connectivity to challenges.cloudflare.com/turnstile/v0/siteverify from the server host (curl test).
- If a proxy is required, configure the standard proxy env vars / global agent so Node's fetch uses it.
- Fix container DNS (resolv.conf, CoreDNS) if the host resolves but the container does not.
- Retry the request after a short backoff — this error is explicitly 'temporarily unavailable'; if it persists, check Cloudflare status.
Example fix
null
Defensive patterns
Strategy: retry
Validate before calling
await fetch('https://challenges.cloudflare.com/turnstile/v0/siteverify', { method: 'HEAD', signal: AbortSignal.timeout(3000) })
.then(() => console.log('turnstile reachable'))
.catch(() => console.error('turnstile unreachable — fix egress/DNS before enabling captcha')); Type guard
const isNetworkError = (e: unknown): e is { code: 'network_error' } =>
!!e && typeof e === 'object' && (e as any).code === 'network_error'; Try / catch
for (let attempt = 1; attempt <= 3; attempt++) {
try {
return await captchaService.verifyRequest(credential, req);
} catch (e) {
if (!isNetworkError(e) || attempt === 3) throw e;
await backoff(attempt * 500);
}
} Prevention
- Smoke-test outbound access to challenges.cloudflare.com during deployment.
- Configure proxy env vars for egress-controlled networks.
- Alert on the captcha_verification{result=unavailable} metric to catch connectivity regressions.
When it happens
Trigger: Server has no outbound internet access (air-gapped or egress-firewalled self-host); Turnstile endpoint blocked by proxy rules; slow networks or Turnstile outages causing the 5s AbortSignal.timeout to abort; DNS resolution failures in the container.
Common situations: Self-hosted instances behind corporate proxies that whitelist only specific domains; Kubernetes pods with broken DNS; transient Cloudflare incidents; CI environments without network.
Related errors
AI-assisted analysis of toeverything/AFFiNE@b4c8548c09 (2026-08-18).
Data as JSON: /api/errors/55a3304acbe3bcdd.
Report an issue: GitHub.