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

  1. Normalize the input to an absolute URL with an explicit https:// scheme before passing it
  2. Trim whitespace and URL-encode spaces in the path
  3. 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

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

Related errors


AI-assisted analysis of ComposioHQ/composio@64b1b85502 (2026-08-28). Data as JSON: /api/errors/3ab43620e9eda8b5. Report an issue: GitHub.