srbhr/Resume-Matcher · error · Error

Request timed out. If you are running a local LLM, increase

Error message

Request timed out. If you are running a local LLM, increase NEXT_PUBLIC_REQUEST_TIMEOUT_MS (and the backend REQUEST_TIMEOUT_SECONDS to match); otherwise try a shorter job description or check your connection.

What it means

apiFetch wraps fetch with an AbortController timeout; when the request is aborted the browser throws an AbortError, which this code converts into a human-readable timeout Error. The message points at NEXT_PUBLIC_REQUEST_TIMEOUT_MS (frontend) and REQUEST_TIMEOUT_SECONDS (backend) as the knobs to tune.

Source

Thrown at apps/frontend/lib/api/client.ts:80

  if (isAbsoluteUrl) {
    url = endpoint;
  } else if (isApiPath) {
    url = resolveRuntimeApiBase(normalizedEndpoint);
  }

  // Defaults to DEFAULT_TIMEOUT_MS, which tracks the backend's
  // REQUEST_TIMEOUT_SECONDS (see next.config.ts proxyTimeout — all three layers
  // must agree or the shortest aborts first).
  const timeout = timeoutMs ?? DEFAULT_TIMEOUT_MS;
  const controller = new AbortController();
  const timer = setTimeout(() => controller.abort(), timeout);

  try {
    return await fetch(url, { ...options, signal: controller.signal });
  } catch (error) {
    if (error instanceof Error && error.name === 'AbortError') {
      throw new Error(
        'Request timed out. If you are running a local LLM, increase NEXT_PUBLIC_REQUEST_TIMEOUT_MS (and the backend REQUEST_TIMEOUT_SECONDS to match); otherwise try a shorter job description or check your connection.'
      );
    }
    throw error;
  } finally {
    clearTimeout(timer);
  }
}

/**
 * POST request with JSON body.
 */
export async function apiPost<T>(endpoint: string, body: T, timeoutMs?: number): Promise<Response> {
  return apiFetch(
    endpoint,
    {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },

View on GitHub (pinned to 116f9cc3b0)

Solutions

  1. Increase NEXT_PUBLIC_REQUEST_TIMEOUT_MS in the frontend env and REQUEST_TIMEOUT_SECONDS in the backend to a value exceeding worst-case LLM latency
  2. Shorten the job description / reduce the workload sent to the LLM
  3. Verify the backend is actually processing (check logs); if it is hung, fix the backend/LLM provider connectivity
  4. Check network path (VPN, proxy) latency between frontend and backend

Example fix

// before (apps/frontend/.env.local)
NEXT_PUBLIC_REQUEST_TIMEOUT_MS=30000
// after
NEXT_PUBLIC_REQUEST_TIMEOUT_MS=300000
Defensive patterns

Strategy: retry

Validate before calling

const timeoutMs = Number(process.env.NEXT_PUBLIC_REQUEST_TIMEOUT_MS);
if (!timeoutMs || timeoutMs < 60000) {
  console.warn('NEXT_PUBLIC_REQUEST_TIMEOUT_MS too low for LLM workloads; set >= 120000');
}

Type guard

function isTimeoutError(e: unknown): e is Error {
  return e instanceof Error && e.message.startsWith('Request timed out');
}

Try / catch

try {
  return await apiPost('/analyze', body);
} catch (e) {
  if (isTimeoutError(e)) {
    // retry once with backoff, or surface tuning hint
    return await withBackoff(() => apiPost('/analyze', body), 1);
  }
  throw e;
}

Prevention

When it happens

Trigger: fetch to any API endpoint exceeds the configured timeout (default NEXT_PUBLIC_REQUEST_TIMEOUT_MS) and the controller aborts — e.g. long LLM generation on POST /analyze, slow local LLM, or stalled network connection.

Common situations: Running a local LLM (Ollama/llama.cpp) that takes minutes on a long job description while the frontend timeout defaults to ~30-60s; backend hanging on an upstream provider; VPN/proxy latency.

Understand the failure class

Related errors


AI-assisted analysis of srbhr/Resume-Matcher@116f9cc3b0 (2026-08-28). Data as JSON: /api/errors/5b2d170b9048b8fe. Report an issue: GitHub.