ComposioHQ/composio · error · ComposioBlockedInternalUrlError

Could not resolve host "${host}"

Error message

Could not resolve host "${host}"

What it means

When fetching a user-supplied URL, the SSRF guard resolves the hostname with DNS lookup() before connecting. If the DNS lookup itself throws (NXDOMAIN, DNSSEC failure, resolver unreachable), the fetch is refused with this error (the lookup-thrown variant).

Source

Thrown at ts/packages/core/src/utils/ssrfGuard.node.ts:192

    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 });
  }

  for (const { address } of resolved) {
    if (isBlockedIp(address)) {
      throw new ComposioBlockedInternalUrlError(
        `Refusing to fetch "${host}" — it resolves to a private, loopback, or link-local address`,
        { url: rawUrl, resolvedIp: address }
      );
    }
  }

  // Every answer was validated, so all of them are safe to connect to, and
  // resolver order is the system's own address preference.
  return resolved.map(({ address }) => address);

View on GitHub (pinned to 64b1b85502)

Solutions

  1. Verify the hostname resolves from the machine running the SDK: nslookup <host> or dns.lookup
  2. For internal hosts, upload the file bytes directly instead of by URL — internal names are intentionally unsupported
  3. Fix typos or use the canonical public hostname

Example fix

// before
await upload.uploadFileAtUrl('https://myapp.local/report.pdf');

// after
// internal host: fetch it yourself and upload the bytes
const bytes = await fetchInternal();
await upload.uploadFile(bytes);
Defensive patterns

Strategy: validation

Validate before calling

import { lookup } from 'node:dns/promises';
try { await lookup(host, { all: true }); } catch { throw new Error('Host unresolvable, reject early'); }

Try / catch

try {
  await upload.uploadFileAtUrl(url);
} catch (e) {
  if (e instanceof ComposioBlockedInternalUrlError && /Could not resolve host/.test(e.message)) {
    // fix hostname or upload bytes directly
  }
}

Prevention

When it happens

Trigger: Passing a URL whose hostname does not exist or cannot be resolved from the server running the SDK — typo'd domains, freshly-dead domains, or environments with broken/filtered DNS.

Common situations: Typos in hostnames; internal hostnames not resolvable from public DNS (myapp.local, internal service names); sandboxed environments without DNS egress.

Understand the failure class

Related errors


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