mihomo-party-org/clash-party · error
Forbidden header: ${k}
Error message
Forbidden header: ${k} What it means
validateHeaders() rejects requests containing headers in the FORBIDDEN_HEADERS set (matched case-insensitively). These headers (typically hop-by-hop or identity/spoofable ones like host, content-length, connection, cookie controls) must be controlled by the HTTP client itself, so user-supplied values are rejected with `Forbidden header: ${k}`.
Source
Thrown at src/main/resolve/plugin/http-client.ts:38
headers: http.IncomingHttpHeaders
body: string
}
const FORBIDDEN_HEADERS = new Set(['host', 'content-length', 'connection', 'transfer-encoding'])
const MAX_HEADERS = 32
const MAX_HEADER_NAME_LEN = 128
const MAX_HEADER_VALUE_LEN = 4096
const MAX_HEADER_BYTES = 16 * 1024
function validateHeaders(input: Record<string, string>): Record<string, string> {
const entries = Object.entries(input)
if (entries.length > MAX_HEADERS) throw new Error('Request headers too large')
const headers: Record<string, string> = {}
let total = 0
for (const [k, v] of entries) {
if (FORBIDDEN_HEADERS.has(k.toLowerCase())) {
throw new Error(`Forbidden header: ${k}`)
}
const nameBytes = Buffer.byteLength(k, 'utf-8')
const valueBytes = Buffer.byteLength(v, 'utf-8')
if (nameBytes > MAX_HEADER_NAME_LEN || valueBytes > MAX_HEADER_VALUE_LEN) {
throw new Error('Request headers too large')
}
total += nameBytes + valueBytes
headers[k] = v
}
if (total > MAX_HEADER_BYTES) throw new Error('Request headers too large')
return headers
}
export function requestOnce(urlStr: string, opts: PluginRequestOptions): Promise<PluginResponse> {
return new Promise((resolve, reject) => {
let url: URL
try {
url = new URL(urlStr)View on GitHub (pinned to 911e090537)
Solutions
- Remove the forbidden header(s) from the headers object before calling.
- If forwarding inbound headers, filter against the forbidden set (case-insensitive) first.
- Let the client set transport-level headers (host, content-length, connection) itself; only pass application-level headers.
Example fix
// before
await request(url, { headers: { host: 'gw.example', 'content-type': 'application/json' } })
// after
await request(url, { headers: { 'content-type': 'application/json' } }) Defensive patterns
Strategy: validation
Validate before calling
const FORBIDDEN = new Set(['host','content-length','connection','transfer-encoding','expect','keep-alive']) const safeHeaders = Object.fromEntries( Object.entries(headers).filter(([k]) => !FORBIDDEN.has(k.toLowerCase())) )
Try / catch
try {
return await request(url, { headers })
} catch (e) {
const m = (e as Error).message.match(/^Forbidden header: (.+)$/)
if (m) {
delete headers[m[1]]
return request(url, { headers })
} else throw e
} Prevention
- Never forward inbound request headers wholesale; filter hop-by-hop/transport headers first.
- Let the HTTP client manage host, content-length, and connection headers.
- Keep a shared FORBIDDEN_HEADERS list in sync when the library updates it.
When it happens
Trigger: Passing any header whose lowercase name is in FORBIDDEN_HEADERS — e.g. { 'Host': 'x', 'content-length': '5', 'Connection': 'keep-alive' } — in the headers argument consumed by requestOnce.
Common situations: Blindly forwarding headers from an inbound request (host, connection, content-length are common), manually setting content-length after the client already computes it, or copying headers from a cURL example that includes forbidden ones.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- Request headers too large
- Invalid core path: directory traversal detected
- Plugin URL must use https
- Plugin URL must not contain userinfo
- Plugin URL must use a public host
AI-assisted analysis of mihomo-party-org/clash-party@911e090537 (2026-08-30).
Data as JSON: /api/errors/95080d6d431506ca.
Report an issue: GitHub.