{"record":{"id":"f1c336ebf9db8f85","repo":"payloadcms/payload","slug":"a-valid-url-string-is-required","errorCode":null,"errorMessage":"A valid URL string is required.","messagePattern":"A valid URL string is required\\.","errorType":"http","errorClass":"APIError","httpStatus":400,"severity":"error","filePath":"packages/payload/src/uploads/endpoints/getFileFromURL.ts","lineNumber":52,"sourceCode":"      throw new Forbidden(req.t)\n    }\n  } else {\n    // creating doc\n    const accessResult = await executeAccess({ slug: config.slug, req }, config.access?.create)\n    if (!accessResult) {\n      throw new Forbidden(req.t)\n    }\n  }\n\n  if (!req.url) {\n    throw new APIError('Request URL is missing.', 400)\n  }\n\n  const { searchParams } = new URL(req.url)\n  const src = searchParams.get('src')\n\n  if (!src || typeof src !== 'string') {\n    throw new APIError('A valid URL string is required.', 400)\n  }\n\n  const hasAllowList =\n    typeof config.upload.pasteURL === 'object' && Array.isArray(config.upload.pasteURL.allowList)\n\n  let fileURL: string\n  try {\n    fileURL = new URL(src).href\n  } catch {\n    throw new APIError('A valid URL string is required.', 400)\n  }\n\n  if (hasAllowList && !isURLAllowed(fileURL, config.upload.pasteURL.allowList)) {\n    throw new APIError('The provided URL is not allowed.', 400)\n  }\n\n  let redirectCount = 0\n  const maxRedirects = 3","sourceCodeStart":34,"sourceCodeEnd":70,"githubUrl":"https://github.com/payloadcms/payload/blob/00c58b35c0ed348ddc22daabf467b139727214fd/packages/payload/src/uploads/endpoints/getFileFromURL.ts#L34-L70","documentation":"APIError (HTTP 400, 'A valid URL string is required.') thrown when the `src` query parameter is missing (null) or not a string. The handler reads searchParams.get('src') and requires a truthy string before attempting URL parsing.","triggerScenarios":"Calling /api/:collection/paste-url without a ?src= query param, with an empty ?src=, or where the param is somehow not a string (array via repeated keys). Triggered after auth and pasteURL-enabled checks pass.","commonSituations":"Frontend forgetting to append ?src=; copy-paste error in the URL; query string dropped by a form encoding bug; ?src= encoded in the path instead of the query.","solutions":["Always include a non-empty ?src=<absolute-url> in the request.","Validate on the client that the URL input is non-empty before issuing the request.","URL-encode the src value to preserve query/embedded chars.","If src must be optional in your flow, fork the endpoint or pre-validate upstream."],"exampleFix":"// before\nfetch(`/api/media/paste-url`, { method: 'POST' })\n// after — include src\nfetch(`/api/media/paste-url?src=${encodeURIComponent(src)}`, {\n  method: 'POST', headers: { Authorization: `JWT ${token}` },\n})","handlingStrategy":"validation","validationCode":"function hasSrcParam(url: string): boolean {\n  try { return Boolean(new URL(url, 'http://x').searchParams.get('src')) } catch { return false }\n}\nif (!hasSrcParam(myUrl)) throw new Error('Add ?src=<url>')","typeGuard":"const hasNonEmptySrc = (search: URLSearchParams): boolean =>\n  typeof search.get('src') === 'string' && (search.get('src') as string).length > 0","tryCatchPattern":"try {\n  await fetch(`/api/media/paste-url?src=${encodeURIComponent(src)}`, { method: 'POST' })\n} catch (e) {\n  if (/A valid URL string is required/.test(e.message)) alert('Provide a source URL')\n}","preventionTips":["Always append ?src= when calling paste-url.","URL-encode the src value.","Validate non-empty on the client before submit.","Disable the submit button when the URL input is empty."],"tags":["upload","paste-url","validation","query-params"],"backgroundTag":null,"analyzedSha":"00c58b35c0ed348ddc22daabf467b139727214fd","analyzedAt":"2026-08-12T20:45:03.758Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}