tinyhumansai/openhuman · error
Request timed out. Please try again.
Error message
Request timed out. Please try again.
What it means
The magic-link send request exceeded its client-side timeout: window.setTimeout aborted the fetch via AbortController, the AbortError was caught in the catch block, and it is rethrown as this friendly timeout message. The request never completed — the backend may or may not have sent the email.
Source
Thrown at app/src/services/api/authApi.ts:40
try {
const versionHeaders = await getClientVersionHeaders();
const response = await fetch(`${backendUrl}/auth/email/send-link`, {
method: 'POST',
headers: { 'Content-Type': 'application/json', ...versionHeaders },
body: JSON.stringify({ email, frontendRedirectUri }),
signal: controller.signal,
});
if (!response.ok) {
const body = (await response.json().catch(() => ({}))) as { error?: string };
throw new Error(body.error ?? `Failed to send magic link (${response.status})`);
}
} catch (error) {
if (
(error instanceof DOMException && error.name === 'AbortError') ||
(error instanceof Error && error.name === 'AbortError')
) {
throw new Error('Request timed out. Please try again.');
}
throw error;
} finally {
window.clearTimeout(timeoutId);
}
}
/**
* Consume a verified login token and return the JWT.
* Works for both Telegram and OAuth login tokens.
* POST /telegram/login-tokens/:token/consume (no auth required)
*/
export async function consumeLoginToken(loginToken: string): Promise<string> {
const response = await callCoreRpc<{ result: { jwtToken: string } }>({
method: 'openhuman.auth.consume_login_token',
params: { loginToken },
});
const jwtToken = response.result?.jwtToken;View on GitHub (pinned to a221052e0d)
Solutions
- Retry the send (the common case is transient) — but warn that a duplicate email may arrive if the first eventually succeeded
- Verify network/backend reachability, then retry once the service responds to /health
- Increase the timeout constant if the backend legitimately needs longer (cold starts)
- Debounce the send button so impatience-driven duplicates don't stack timeouts
Example fix
// before
await sendMagicLink(email);
// after
try {
await sendMagicLink(email);
} catch (e) {
if (String(e.message).includes('timed out')) {
showNotice('The request timed out. Check your connection and try again.');
return;
}
throw e;
} Defensive patterns
Strategy: retry
Try / catch
const withTimeoutRetry = async (fn: () => Promise<void>, tries = 2) => {
for (let i = 0; i < tries; i++) {
try { return await fn(); }
catch (e) {
if (!String((e as Error).message).includes('timed out') || i === tries - 1) throw e;
await new Promise(r => setTimeout(r, 1000 * (i + 1)));
}
}
};
await withTimeoutRetry(() => sendMagicLink(email)); Prevention
- Disable the send button while a request is in flight to prevent stacking timeouts
- Check backend reachability first when timeouts repeat
- Raise the timeout constant if backend cold starts routinely exceed it
When it happens
Trigger: Slow or unreachable backend (fetch pending past timeoutMs); dev machine offline/VPN-blocked; backend cold start taking longer than the configured timeout. Any AbortError named signal from this controller maps here.
Common situations: Local backend still compiling when the login screen is used; network flakiness on mobile; an aggressive timeout constant after a latency regression; suspend/resume of the machine mid-request.
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- Failed to send magic link (${response.status})
- [transport:cloud] ${method} timed out after ${this.timeoutMs
- [transport:lan] ${method} timed out after ${this.timeoutMs}m
- RPC ${method} timed out after ${timeoutMs}ms
- RPC ${method} timed out after ${timeoutMs}ms
AI-assisted analysis of tinyhumansai/openhuman@a221052e0d (2026-08-16).
Data as JSON: /api/errors/c143cea1a516b161.
Report an issue: GitHub.