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
- Verify frpc is running with webServer (admin) enabled and the dashboard targets that exact host:port
- Reproduce the failing call with curl against the admin API to see the raw status code
- Inspect err.status in the catch block — the message alone only carries the number
- 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
- Health-check the admin endpoint once at dashboard startup and surface a clear 'frpc unreachable' state
- Centralize all API calls through the http wrapper so HTTPError status handling lives in one interceptor
- Keep dashboard and frpc versions in lockstep so endpoint paths always match
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
- HTTP ${response.status}
- invalid argument: body can't be empty
- envelope?.msg || `HTTP ${response.status}`
- Invalid API v2 response
- envelope.msg
AI-assisted analysis of fatedier/frp@6c8a8d0a97 (2026-08-15).
Data as JSON: /api/errors/f20e1f04a7e114f1.
Report an issue: GitHub.