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

  1. Use the local-file upload API for files on disk instead of a file:// URL
  2. Convert ftp/data sources to an https endpoint first, or download and upload bytes directly
  3. 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

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


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