ComposioHQ/composio · error · ComposioBlockedInternalUrlError
Refusing to fetch a malformed URL
Error message
Refusing to fetch a malformed URL
What it means
Part of the SSRF guard for URL-based file uploads: assertSafeFetchTarget parses the target URL and refuses to proceed if it cannot be parsed as a URL at all. Failing closed prevents accidental fetches of malformed targets.
Source
Thrown at ts/packages/core/src/utils/ssrfGuard.node.ts:176
if (family === 6) return isBlockedIpv6(ip);
return true;
};
/**
* 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 });
}
View on GitHub (pinned to 64b1b85502)
Solutions
- Normalize the input to an absolute URL with an explicit https:// scheme before passing it
- Trim whitespace and URL-encode spaces in the path
- Validate with new URL(url) in your own code before calling the upload API
Example fix
// before
await upload.uploadFileAtUrl('example.com/files/report.pdf');
// after
await upload.uploadFileAtUrl('https://example.com/files/report.pdf'); Defensive patterns
Strategy: validation
Validate before calling
const isAbsoluteHttpUrl = (u: string) => { try { const x = new URL(u); return x.protocol === 'https:'; } catch { return false; } }; Type guard
const isParsableUrl = (u: unknown): u is string =>
typeof u === 'string' && u.trim().length > 0 && (() => { try { new URL(u); return true; } catch { return false; } })(); Try / catch
try {
await upload.uploadFileAtUrl(url);
} catch (e) {
if (e instanceof ComposioBlockedInternalUrlError && /malformed/.test(e.message)) {
url = new URL(url.trim()).toString(); // then retry with normalized URL
}
} Prevention
- Normalize user/LLM input with new URL() and require https before calling upload APIs
- Trim and encode URL strings at the input boundary
- Reject scheme-less URLs in your own validation layer
When it happens
Trigger: Passing a URL to a URL-upload API (e.g. uploading a file by URL) that is not parseable by new URL() — missing scheme ('example.com/file'), stray whitespace/characters, or a relative path.
Common situations: LLM-generated URLs without a scheme; user input pasted with leading spaces; concatenating strings that produce '/path' instead of 'https://host/path'.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- Refusing to fetch a non-http(s) URL (scheme "${url.protocol}
- 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/3ab43620e9eda8b5.
Report an issue: GitHub.