ComposioHQ/composio · error · ComposioBlockedInternalUrlError
Refusing to fetch a non-http(s) URL (scheme "${url.protocol}
Error message
Refusing to fetch a non-http(s) URL (scheme "${url.protocol}") What it means
The SSRF guard only fetches http: and https: URLs. Any other scheme (file:, ftp:, data:, gopher:, etc.) is rejected before any connection is made, since non-HTTP transports can bypass network policy.
Source
Thrown at ts/packages/core/src/utils/ssrfGuard.node.ts:180
/**
* Validate a single URL: it must be http(s), its host must resolve, and every
* resolved address must be publicly routable. Throws
* {@link ComposioBlockedInternalUrlError} otherwise.
*
* @returns the validated addresses to connect to, in resolver order. Callers
* must connect to *those* rather than let the client resolve the hostname
* again; see {@link ssrfSafeFetch}.
*/
export const assertSafeFetchTarget = async (rawUrl: string): Promise<string[]> => {
let url: URL;
try {
url = new URL(rawUrl);
} catch {
throw new ComposioBlockedInternalUrlError('Refusing to fetch a malformed URL', { url: rawUrl });
}
if (url.protocol !== 'http:' && url.protocol !== 'https:') {
throw new ComposioBlockedInternalUrlError(
`Refusing to fetch a non-http(s) URL (scheme "${url.protocol}")`,
{ url: rawUrl }
);
}
const host = url.hostname.replace(/^\[|\]$/g, '');
let resolved: Array<{ address: string }>;
try {
resolved = await lookup(host, { all: true, verbatim: true });
} catch {
throw new ComposioBlockedInternalUrlError(`Could not resolve host "${host}"`, { url: rawUrl });
}
if (resolved.length === 0) {
throw new ComposioBlockedInternalUrlError(`Could not resolve host "${host}"`, { url: rawUrl });
}
View on GitHub (pinned to 64b1b85502)
Solutions
- Use the local-file upload API for files on disk instead of a file:// URL
- Convert ftp/data sources to an https endpoint first, or download and upload bytes directly
- Validate url.protocol === 'https:' client-side before calling
Example fix
// before
await upload.uploadFileAtUrl('file:///etc/hosts');
// after
// upload local files via the disk-based API
await upload.readFileFromDisk('/path/to/file'); Defensive patterns
Strategy: type-guard
Validate before calling
if (!/^https?:\/\//i.test(url)) throw new Error('Only http(s) URLs are supported'); Type guard
const isHttpUrl = (u: string): boolean => { try { const p = new URL(u).protocol; return p === 'http:' || p === 'https:'; } catch { return false; } }; Try / catch
try {
await upload.uploadFileAtUrl(url);
} catch (e) {
if (e instanceof ComposioBlockedInternalUrlError && e.message.includes('non-http(s)')) {
// use the local-file upload API for file:// targets
}
} Prevention
- Validate scheme client-side before any URL upload
- Use the disk-based API for local files instead of file:// URLs
- Whitelist https only in your input validation
When it happens
Trigger: Passing a URL with a scheme other than http/https to a URL-upload API — e.g. 'file:///etc/passwd', 'ftp://host/file', or a data: URI.
Common situations: LLM or user input supplying file:// paths expecting local upload semantics; legacy ftp endpoints; accidentally prefixing data URIs.
Related errors
- Refusing to fetch a malformed URL
- Refusing to fetch a malformed or non-http(s) URL
- Refusing to fetch "${host}" — it resolves to a private, loop
- Refusing to upload: {reason}. Set sensitive_file_upload_prot
- Refusing to fetch "{parsed.hostname}" because it resolves to
AI-assisted analysis of ComposioHQ/composio@64b1b85502 (2026-08-28).
Data as JSON: /api/errors/1baf718c1477f3d8.
Report an issue: GitHub.