{"record":{"id":"e2c6c100c3201b58","repo":"ComposioHQ/composio","slug":"could-not-resolve-host-host","errorCode":null,"errorMessage":"Could not resolve host \"${host}\"","messagePattern":"Could not resolve host \"(.+?)\"","errorType":"exception","errorClass":"ComposioBlockedInternalUrlError","httpStatus":null,"severity":"error","filePath":"ts/packages/core/src/utils/ssrfGuard.node.ts","lineNumber":192,"sourceCode":"    url = new URL(rawUrl);\n  } catch {\n    throw new ComposioBlockedInternalUrlError('Refusing to fetch a malformed URL', { url: rawUrl });\n  }\n\n  if (url.protocol !== 'http:' && url.protocol !== 'https:') {\n    throw new ComposioBlockedInternalUrlError(\n      `Refusing to fetch a non-http(s) URL (scheme \"${url.protocol}\")`,\n      { url: rawUrl }\n    );\n  }\n\n  const host = url.hostname.replace(/^\\[|\\]$/g, '');\n\n  let resolved: Array<{ address: string }>;\n  try {\n    resolved = await lookup(host, { all: true, verbatim: true });\n  } catch {\n    throw new ComposioBlockedInternalUrlError(`Could not resolve host \"${host}\"`, { url: rawUrl });\n  }\n\n  if (resolved.length === 0) {\n    throw new ComposioBlockedInternalUrlError(`Could not resolve host \"${host}\"`, { url: rawUrl });\n  }\n\n  for (const { address } of resolved) {\n    if (isBlockedIp(address)) {\n      throw new ComposioBlockedInternalUrlError(\n        `Refusing to fetch \"${host}\" — it resolves to a private, loopback, or link-local address`,\n        { url: rawUrl, resolvedIp: address }\n      );\n    }\n  }\n\n  // Every answer was validated, so all of them are safe to connect to, and\n  // resolver order is the system's own address preference.\n  return resolved.map(({ address }) => address);","sourceCodeStart":174,"sourceCodeEnd":210,"githubUrl":"https://github.com/ComposioHQ/composio/blob/64b1b85502b1beeb2379e6c9e8bf1104504fa637/ts/packages/core/src/utils/ssrfGuard.node.ts#L174-L210","documentation":"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).","triggerScenarios":"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.","commonSituations":"Typos in hostnames; internal hostnames not resolvable from public DNS (myapp.local, internal service names); sandboxed environments without DNS egress.","solutions":["Verify the hostname resolves from the machine running the SDK: nslookup <host> or dns.lookup","For internal hosts, upload the file bytes directly instead of by URL — internal names are intentionally unsupported","Fix typos or use the canonical public hostname"],"exampleFix":"// before\nawait upload.uploadFileAtUrl('https://myapp.local/report.pdf');\n\n// after\n// internal host: fetch it yourself and upload the bytes\nconst bytes = await fetchInternal();\nawait upload.uploadFile(bytes);","handlingStrategy":"validation","validationCode":"import { lookup } from 'node:dns/promises';\ntry { await lookup(host, { all: true }); } catch { throw new Error('Host unresolvable, reject early'); }","typeGuard":null,"tryCatchPattern":"try {\n  await upload.uploadFileAtUrl(url);\n} catch (e) {\n  if (e instanceof ComposioBlockedInternalUrlError && /Could not resolve host/.test(e.message)) {\n    // fix hostname or upload bytes directly\n  }\n}","preventionTips":["Verify hostnames resolve publicly before offering them as upload URLs","Don't pass internal (.local, service names) hosts to URL uploads","Upload bytes directly when the source is internal"],"tags":["ssrf","dns","network","url-validation"],"backgroundTag":"dns-resolution-failed","analyzedSha":"64b1b85502b1beeb2379e6c9e8bf1104504fa637","analyzedAt":"2026-08-28T15:39:33.623Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}