{"record":{"id":"afb4fc685c657285","repo":"transloadit/uppy","slug":"url-server-responded-with-status-urlmeta-status","errorCode":null,"errorMessage":"URL server responded with status: ${urlMeta.statusCode}","messagePattern":"URL server responded with status: (.+?)","errorType":"http","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/@uppy/companion/src/server/helpers/request.ts","lineNumber":253,"sourceCode":"    )\n  }\n\n  // We prefer to use a HEAD request, as it doesn't download the content. If the URL doesn't\n  // support HEAD, or doesn't follow the spec and provide the correct Content-Length, we\n  // fallback to GET.\n  let urlMeta = await requestWithMethod('HEAD')\n\n  // If HTTP error response, we retry with GET, which may work on non-compliant servers\n  // (e.g. HEAD doesn't work on signed S3 URLs)\n  // We look for status codes in the 400 and 500 ranges here, as 3xx errors are\n  // unlikely to have to do with our choice of method\n  // todo add unit test for this\n  if (urlMeta.statusCode >= 400 || urlMeta.size === 0 || urlMeta.size == null) {\n    urlMeta = await requestWithMethod('GET')\n  }\n\n  if (urlMeta.statusCode >= 300) {\n    throw new Error(`URL server responded with status: ${urlMeta.statusCode}`)\n  }\n\n  const { name, size, type } = urlMeta\n  return { name, size, type }\n}\n","sourceCodeStart":235,"sourceCodeEnd":259,"githubUrl":"https://github.com/transloadit/uppy/blob/5d4dedd02a1ac0ae022c75c54aca76558f88e256/packages/@uppy/companion/src/server/helpers/request.ts#L235-L259","documentation":"Companion's getURLMeta fetches metadata (name, size, type) for a remote URL via a HEAD request (falling back to GET when the HEAD response is >=400 or has no size). If the final response status code is 300 or above, the remote server is considered to have failed and this error is thrown, embedding the status code in the message. It surfaces through Companion's /url/meta endpoint when a user adds a file by URL.","triggerScenarios":"Calling POST /url/meta (or the uppy.addUrl / addFiles with remote URL flow) where the target server responds with a redirect that isn't followed, a 4xx/5xx to both HEAD and GET, or the URL points to a page that doesn't serve the file directly (e.g. an expired signed link, a login page returning 403, or an HTML error page).","commonSituations":"Expired pre-signed S3/Azure URLs, links behind authentication that return 401/403, servers that reject HEAD requests with 405 and also fail GET, or copy-pasted URLs with typos producing 404. Also occurs when the remote host blocks Companion's user agent or IP.","solutions":["Verify the URL responds with 2xx from a server context (curl -I <url>) — if not, the URL itself is the problem (expired/typo/auth-required)","If the server rejects HEAD with 4xx/5xx, ensure it serves valid Content-Length and Content-Type on GET so the fallback succeeds","If the host blocks Companion's requests (firewall/UA filtering), allowlist Companion's IP or user-agent, or proxy the download through an endpoint that responds correctly","Catch this error in the client and surface a user-facing message telling them the link is unreachable"],"exampleFix":"// before\nconst meta = await companionClient.urlMeta('https://cdn.example.com/file.pdf')\n\n// after\ntry {\n  const meta = await companionClient.urlMeta('https://cdn.example.com/file.pdf')\n} catch (err) {\n  if (/URL server responded with status/.test(err.message)) {\n    throw new Error('This link could not be reached. It may be expired or require login.')\n  }\n  throw err\n}","handlingStrategy":"try-catch","validationCode":"// Pre-check reachability before handing the URL to Companion\nasync function urlIsReachable(url) {\n  const res = await fetch(url, { method: 'HEAD' }).catch(() => null)\n  if (!res || res.status >= 300) return false\n  const len = Number(res.headers.get('content-length'))\n  return Number.isFinite(len) && len > 0\n}","typeGuard":"function isUrlMetaError(err: unknown): boolean {\n  return err instanceof Error && /^URL server responded with status: \\d+$/.test(err.message)\n}","tryCatchPattern":"try {\n  const { name, size, type } = await uppy.getPlugin('Url').getUrlMeta(url)\n} catch (err) {\n  if (isUrlMetaError(err)) return notifyUser('This link is unreachable or expired.')\n  throw err\n}","preventionTips":["Validate URLs client-side (format + reachability) before submitting to /url/meta","For signed links, check expiry timestamps before use","Monitor failure rates per host to catch allowlisting/firewall issues early"],"tags":["companion","url","metadata","http-status"],"backgroundTag":"http-request-failed","analyzedSha":"5d4dedd02a1ac0ae022c75c54aca76558f88e256","analyzedAt":"2026-08-28T12:18:41.267Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}