TryGhost/Ghost · error · TimeoutError

Request timed out, please try again.

Error message

Request timed out, please try again.

What it means

TimeoutError, thrown by the fetchApi retry loop when the underlying fetch rejects with an AbortError. The caller passes a `timeout` option; after that many ms an AbortController aborts the request (fetch-api.ts:216), fetch rejects as AbortError, and the loop re-throws it as a TimeoutError. Note: AbortError is NOT in retryableErrors ([ServerUnreachableError, MaintenanceError, TypeError]), so it is never retried — it surfaces on the first abort.

Source

Thrown at apps/admin-x-framework/src/utils/api/fetch-api.ts:240

                    // Awaited so response errors reject inside the try/catch
                    return await handleResponse(response) as ResponseData;
                } catch (error) {
                    retryingMs = Date.now() - startTime;

                    if (retry && (import.meta.env.MODE !== 'development' && retryableErrors.some(errorClass => error instanceof errorClass) && retryingMs <= maxRetryingMs)) {
                        await new Promise((resolve) => {
                            setTimeout(resolve, retryPeriods[attempts] || retryPeriods[retryPeriods.length - 1]);
                        });
                        attempts += 1;
                        continue;
                    }

                    if (attempts !== 0 && sentryDSN) {
                        Sentry.captureMessage('Request failed after multiple attempts', {extra: getErrorData()});
                    }

                    if (error && typeof error === 'object' && 'name' in error && error.name === 'AbortError') {
                        throw new TimeoutError();
                    }

                    if (error instanceof UnauthorizedError && isSessionExpiry(endpoint)) {
                        redirectOnSessionExpiry();
                        throw new SessionExpiredError(error.response!, error.data, {cause: error});
                    }

                    let newError = error;

                    if (!(error instanceof APIError)) {
                        newError = new ServerUnreachableError({cause: error});
                    }

                    throw newError;
                };
            }
        } finally {
            clearTimeout(timeoutHandle);

View on GitHub (pinned to 47d8b0e2ad)

Solutions

  1. Increase the `timeout` option passed to the API call for endpoints known to be slow.
  2. Catch TimeoutError specifically and retry the operation idempotently (the library does not retry aborts).
  3. Investigate server-side latency (DB slow queries, long-running jobs) if timeouts are recurring on fast endpoints.

Example fix

// before
const {data, error} = useBrowsePosts({timeout: 5000});

// after
const {data, error} = useBrowsePosts({timeout: 30_000});
// and in an effect/handler:
if (error instanceof TimeoutError) { /* retry once */ }
Defensive patterns

Strategy: try-catch

Type guard

import {TimeoutError} from '@tryghost/admin-x-framework/utils/errors';
function isTimeoutError(e: unknown): e is TimeoutError {
    return e instanceof TimeoutError;
}

Try / catch

import {TimeoutError} from '@tryghost/admin-x-framework/utils/errors';
try {
    await someApiCall({timeout: 30_000});
} catch (e) {
    if (e instanceof TimeoutError) {
        // optional single retry — the library does not retry aborts
        await someApiCall({timeout: 30_000});
    } else {
        throw e;
    }
}

Prevention

When it happens

Trigger: A useBrowse/useEdit/etc. hook (or direct fetchApi call) is invoked with a `timeout` option and the Ghost API takes longer than that to respond. The setTimeout fires controller.abort(), the in-flight fetch rejects with name === 'AbortError', and this throw executes.

Common situations: Slow server responses on large member/list exports; a stalled request during a network brownout where the server accepted the connection but never responded; a too-aggressive timeout value passed for an endpoint known to be slow (uploads, theme activation, imports).

Understand the failure class

Related errors


AI-assisted analysis of TryGhost/Ghost@47d8b0e2ad (2026-08-13). Data as JSON: /api/errors/0a0bf858d540643a. Report an issue: GitHub.