fatedier/frp · error · HTTPError
HTTP ${response.status}
Error message
HTTP ${response.status} What it means
Generic non-2xx failure from the frps dashboard's plain fetch wrapper (request()). Any frps API call that receives a non-OK HTTP status throws an HTTPError with message 'HTTP <status>'. The frps dashboard API sits behind optional basic auth (dashboard.user/dashboard.password) and requires the dashboard port, not the bind port.
Source
Thrown at web/frps/src/api/http.ts:37
export interface V2Page<T> {
total: number
page: number
pageSize: number
items: T[]
}
type QueryParamValue = string | number | boolean | null | undefined
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
}
return response.json()
}
async function requestV2<T>(
url: string,
options: RequestInit = {},
): Promise<T> {View on GitHub (pinned to 6c8a8d0a97)
Solutions
- Confirm the URL uses frps webServer.port (dashboard port), not the proxy bindPort
- Provide dashboard basic-auth credentials (the wrapper relies on credentials:'include' cookies from the login flow)
- Check err.status in the catch: 401 → auth, 404 → wrong path or old frps, 5xx → server logs
- Upgrade frps so its API surface matches the dashboard's expected endpoints
Example fix
// before
const info = await http.get('/api/serverinfo')
// after
const info = await http.get('/api/serverinfo', {
headers: { Authorization: 'Basic ' + btoa(`${user}:${pass}`) },
}) Defensive patterns
Strategy: try-catch
Validate before calling
// Confirm the frps dashboard port responds before the first API call
const probe = await fetch(`${base}/api/serverinfo`, { credentials: 'include' })
if (probe.status === 401) await login() // establish basic-auth/session first Type guard
function isHTTPError(e: unknown): e is { status: number } & Error {
return e instanceof Error && typeof (e as any).status === 'number'
} Try / catch
try {
return await http.get<T>('/api/serverinfo')
} catch (e) {
if (isHTTPError(e)) {
switch (e.status) {
case 401: return handleAuthExpired()
case 404: return handleWrongEndpoint(e)
}
}
throw e
} Prevention
- Authenticate before the first data fetch and re-auth on 401 rather than letting raw HTTP errors surface
- Target webServer.port (dashboard port), never the proxy bindPort
- Log e.status alongside the message — 'HTTP 500' alone is diagnosable only with the number
When it happens
Trigger: Calling frps API endpoints (/api/serverinfo, /api/proxy/tcp, etc.) when the response status is not 2xx: 401 when dashboard basic-auth credentials are wrong or credentials:'include' cookies are missing, 404 for an endpoint that does not exist on that frps version, 500 on server-side handler errors.
Common situations: Accessing the API port instead of the dashboard port (bindPort vs webServer.port); dashboard.password set but the request sent no credentials; older frps version that lacks newer API routes; reverse proxy stripping Authorization headers.
Related errors
- HTTP ${response.status}
- envelope?.msg || `HTTP ${response.status}`
- Invalid API v2 response
- envelope.msg
- %s
AI-assisted analysis of fatedier/frp@6c8a8d0a97 (2026-08-15).
Data as JSON: /api/errors/391f81fe54a6df2d.
Report an issue: GitHub.