ruvnet/ruflo · error · HttpFetchValidationError
FORBIDDEN_PROTOCOL
FORBIDDEN_PROTOCOL
Error message
protocol ${parsed.protocol} not allowed (only http: and https:) What it means
HttpFetchValidationError with code FORBIDDEN_PROTOCOL, thrown by validateUrl() (http-fetch-tools.ts:55) when the URL parses but its scheme is anything other than http: or https:. The http_fetch tool is an internet fetcher, not a local file or arbitrary-scheme reader, so file://, ftp://, data:, ws:, chrome:, and every other protocol are refused before any connection is attempted.
Solutions
- Serve the resource over http(s) — for local files, run a local HTTP server and fetch http://127.0.0.1:PORT (that then hits the PRIVATE_ADDRESS guard, so set CLAUDE_FLOW_HTTP_FETCH_ALLOW_PRIVATE=1 if intended)
- Read local files with your own code (fs) instead of http_fetch — it is not a file reader
- Replace ftp:// sources with an https mirror
Example fix
// before
await callTool('http_fetch', { url: 'file:///home/me/notes.md' });
// throws: protocol file: not allowed (FORBIDDEN_PROTOCOL)
// after: read local files outside the fetch tool
import { readFile } from 'node:fs/promises';
const content = await readFile('/home/me/notes.md', 'utf8'); Defensive patterns
Strategy: validation
Validate before calling
function isHttpUrl(raw: string): boolean {
try {
const proto = new URL(raw).protocol.toLowerCase();
return proto === 'http:' || proto === 'https:';
} catch { return false; }
}
if (!isHttpUrl(url)) throw new TypeError('only absolute http/https URLs are fetchable'); Try / catch
try {
await callTool('http_fetch', { url });
} catch (e) {
if (e instanceof HttpFetchValidationError && e.code === 'FORBIDDEN_PROTOCOL' && url.startsWith('file:')) {
// fall back to local file reading outside the fetch tool
return readFile(fileUrlToPath(url), 'utf8');
}
throw e;
} Prevention
- Remember http_fetch is an internet fetcher, not a file reader — use fs APIs for local files
- Normalize legacy ftp:// sources to https mirrors during data migration
When it happens
Trigger: http_fetch with 'file:///etc/hostname', 'file:///home/user/report.html', 'ftp://legacy-host/pub/data.csv', or 'data:text/html,...'. The protocol comparison is lowercased, so casing is not the issue — only http and https pass.
Common situations: Trying to read a local file through the fetch tool; legacy ftp links from old documentation; expecting fetch()-like tolerance (browser fetch also rejects file://, but users try anyway); attempting data: URLs for inline payloads.
Related errors
- INVALID_URL
- Dangerous key segment rejected
- FORBIDDEN_HEADER
- HTTP transport requires host and port configuration
- Key contains disallowed characters
AI-assisted analysis of ruvnet/ruflo@fa13ee4ad6 (2026-08-18).
Data as JSON: /api/errors/523b891fab58a0bd.
Report an issue: GitHub.
Appendix: source
Thrown at v3/@claude-flow/cli/src/mcp-tools/http-fetch-tools.ts:55
this.name = 'HttpFetchValidationError';
}
}
/**
* Decide whether the URL is permitted under the default secure-by-default
* allowlist. Block file://, ftp://, RFC-1918 private addresses, loopback,
* link-local — unless CLAUDE_FLOW_HTTP_FETCH_ALLOW_PRIVATE=1 is set.
*/
export function validateUrl(rawUrl: string): URL {
let parsed: URL;
try {
parsed = new URL(rawUrl);
} catch {
throw new HttpFetchValidationError(`invalid URL: ${rawUrl}`, 'INVALID_URL');
}
const proto = parsed.protocol.toLowerCase();
if (proto !== 'http:' && proto !== 'https:') {
throw new HttpFetchValidationError(
`protocol ${parsed.protocol} not allowed (only http: and https:)`,
'FORBIDDEN_PROTOCOL',
);
}
const host = parsed.hostname.toLowerCase();
const allowPrivate = process.env.CLAUDE_FLOW_HTTP_FETCH_ALLOW_PRIVATE === '1';
if (!allowPrivate && isPrivateOrLoopback(host)) {
throw new HttpFetchValidationError(
`host ${host} is loopback/private/link-local; set CLAUDE_FLOW_HTTP_FETCH_ALLOW_PRIVATE=1 to override`,
'PRIVATE_ADDRESS',
);
}
return parsed;
}
function isPrivateOrLoopback(host: string): boolean {
if (host === 'localhost' || host === 'localhost.localdomain') return true;
// IPv6 loopbackView on GitHub (pinned to fa13ee4ad6)