fatedier/frp · error · HTTPError

HTTP ${response.status}

Error message

HTTP ${response.status}

What it means

Generic non-2xx failure from the frpc web dashboard's fetch wrapper. Every http.get/post/put/delete call in web/frpc/src/api goes through request(), and any response where response.ok is false throws an HTTPError whose message is just 'HTTP <status>'. The frpc admin API (admin_addr/admin_port) returns 4xx/5xx for bad payloads, unknown resources, or when the admin server is not enabled.

Source

Thrown at web/frpc/src/api/http.ts:22

  status: number
  statusText: string

  constructor(status: number, statusText: string, message?: string) {
    super(message || statusText)
    this.status = status
    this.statusText = statusText
  }
}

async function request<T>(url: string, options: RequestInit = {}): Promise<T> {
  const defaultOptions: RequestInit = {
    credentials: 'include',
  }

  const response = await fetch(url, { ...defaultOptions, ...options })

  if (!response.ok) {
    throw new HTTPError(
      response.status,
      response.statusText,
      `HTTP ${response.status}`,
    )
  }

  // Handle empty response (e.g. 204 No Content)
  if (response.status === 204) {
    return {} as T
  }

  const contentType = response.headers.get('content-type')
  if (contentType && contentType.includes('application/json')) {
    return response.json()
  }
  return response.text() as unknown as T
}

View on GitHub (pinned to 6c8a8d0a97)

Solutions

  1. Verify frpc is running with webServer (admin) enabled and the dashboard targets that exact host:port
  2. Reproduce the failing call with curl against the admin API to see the raw status code
  3. Inspect err.status in the catch block — the message alone only carries the number
  4. For 401/403 check webServer.user/webServer.password credentials; for 404 check the endpoint path and frpc version

Example fix

// before
const proxies = await http.get('/api/proxy') // throws HTTP 404

// after
try {
  const proxies = await http.get('/api/proxy')
} catch (e) {
  if (e instanceof Error && 'status' in e) {
    console.error('admin API failed with status', (e as any).status)
  }
  throw e
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Verify the frpc admin API is reachable before issuing calls
async function assertAdminApiUp(base: string) {
  const r = await fetch(`${base}/api/status`, { credentials: 'include' })
  if (!r.ok) throw new Error(`frpc admin API not ready: HTTP ${r.status}`)
}

Type guard

function isHTTPError(e: unknown): e is { status: number; statusText: string } & Error {
  return e instanceof Error && typeof (e as any).status === 'number'
}

Try / catch

try {
  const data = await http.get<T>('/api/proxy')
} catch (e) {
  if (isHTTPError(e) && e.status === 404) return null
  if (isHTTPError(e) && e.status === 401) { await relogin(); throw e }
  throw e
}

Prevention

When it happens

Trigger: Calling any frpc admin API endpoint (e.g. /api/proxy, /api/config, /api/status) while frpc's webServer/admin port returns a non-2xx status: 404 for an unknown route on the admin port, 401/403 when authentication is required, 500 when the config manager fails (e.g. invalid config body).

Common situations: Pointing the frpc dashboard at a port that is actually frps (or another service) so every route 404s; frpc started without webServer.serverPort so nothing is listening; an expired admin session; curl-level proxies returning 502 while developing the dashboard.

Related errors


AI-assisted analysis of fatedier/frp@6c8a8d0a97 (2026-08-15). Data as JSON: /api/errors/f20e1f04a7e114f1. Report an issue: GitHub.