ruvnet/ruflo · error · HttpFetchValidationError
INVALID_HEADER_VALUE
INVALID_HEADER_VALUE
Error message
header "${key}" must be a string What it means
Thrown by validateHeaders when a header value is not a string. HTTP headers must be string-typed for fetch; numbers, booleans, objects, or arrays are rejected to avoid silent coercion bugs and Node fetch type errors downstream. The error carries code INVALID_HEADER_VALUE and names the offending header key.
Source
Thrown at v3/@claude-flow/cli/src/mcp-tools/http-fetch-tools.ts:116
const out: Record<string, string> = {};
for (const [key, value] of Object.entries(headers)) {
const lower = key.toLowerCase();
if (!allowAuth) {
if ((FORBIDDEN_HEADERS_EXACT as readonly string[]).includes(lower)) {
throw new HttpFetchValidationError(
`header "${key}" is not allowed without CLAUDE_FLOW_HTTP_FETCH_ALLOW_AUTH=1`,
'FORBIDDEN_HEADER',
);
}
if (FORBIDDEN_HEADER_PREFIXES.some((p) => lower.startsWith(p))) {
throw new HttpFetchValidationError(
`header "${key}" is not allowed without CLAUDE_FLOW_HTTP_FETCH_ALLOW_AUTH=1`,
'FORBIDDEN_HEADER',
);
}
}
if (typeof value !== 'string') {
throw new HttpFetchValidationError(
`header "${key}" must be a string`,
'INVALID_HEADER_VALUE',
);
}
out[key] = value;
}
return out;
}
function clampNumber(raw: unknown, defaultValue: number, max: number): number {
if (raw === undefined || raw === null) return defaultValue;
const n = Number(raw);
if (!Number.isFinite(n) || n <= 0) return defaultValue;
return Math.min(Math.floor(n), max);
}
export interface HttpFetchResult {
success: boolean;View on GitHub (pinned to 6b01dc5a68)
Solutions
- Stringify every header value: String(value) or template literals.
- Move non-string metadata out of headers into the request body or a separate field.
- Validate headers with an object-string-values type guard before calling http_fetch.
- For JSON content, set 'content-type': 'application/json' and pass the JSON string in the body, not headers.
Example fix
// before
http_fetch({ url, headers: { 'x-retry': 3 } })
// after
http_fetch({ url, headers: { 'x-retry': String(3) } }) Defensive patterns
Strategy: type-guard
Validate before calling
function stringifyHeaders(headers) {
const out = {};
for (const [k, v] of Object.entries(headers)) out[k] = typeof v === 'string' ? v : String(v);
return out;
} Type guard
function isStringHeaderRecord(h: unknown): h is Record<string, string> {
return typeof h === 'object' && h !== null && !Array.isArray(h)
&& Object.values(h).every((v) => typeof v === 'string');
} Prevention
- Type header objects as Record<string,string>.
- Stringify numeric/boolean values at the call site.
- Keep metadata out of headers.
When it happens
Trigger: Passing a headers object where any value is a number (e.g. timeout: 30), boolean, object, array, or null. The check runs after the forbidden-header gate, so a forbidden header with a non-string value throws FORBIDDEN_HEADER first.
Common situations: Passing a numeric retry/timeout as a header; a config object mistakenly nested in headers; a boolean flag; JSON objects intended as header metadata; Date objects.
Related errors
- INVALID_HEADERS
- INVALID_URL
- FORBIDDEN_HEADER
- candidate ${label} must ingest at least one vector
- records must be a non-empty array of {id?, vector, text?}
AI-assisted analysis of ruvnet/ruflo@6b01dc5a68 (2026-08-12).
Data as JSON: /api/errors/74b9fc4d9b8d210a.
Report an issue: GitHub.