{"record":{"id":"c5c1bcc503b0092f","repo":"apify/crawlee","slug":"an-extracted-url-href-is-relative-and-baseurl","errorCode":null,"errorMessage":"An extracted URL: ${href} is relative and baseUrl is not set. Provide a baseUrl to automatically resolve relative URLs.","messagePattern":"An extracted URL: (.+?) is relative and baseUrl is not set\\. Provide a baseUrl to automatically resolve relative URLs\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/utils/src/internals/cheerio.ts","lineNumber":110,"sourceCode":" * @return An array of absolute URLs\n */\nexport function extractUrlsFromCheerio($: CheerioAPI, selector = 'a', baseUrl = ''): string[] {\n    const base = $('base').attr('href');\n    const absoluteBaseUrl = base && tryAbsoluteURL(base, baseUrl);\n\n    if (absoluteBaseUrl) {\n        baseUrl = absoluteBaseUrl;\n    }\n\n    return $(selector)\n        .map((_i, el) => $(el).attr('href'))\n        .get()\n        .filter(Boolean)\n        .map((href) => {\n            // Throw a meaningful error when only a relative URL would be extracted instead of waiting for the Request to fail later.\n            const isHrefAbsolute = /^[a-z][a-z0-9+.-]*:/.test(href); // Grabbed this in 'is-absolute-url' package.\n            if (!isHrefAbsolute && !baseUrl) {\n                throw new Error(\n                    `An extracted URL: ${href} is relative and baseUrl is not set. ` +\n                        'Provide a baseUrl to automatically resolve relative URLs.',\n                );\n            }\n            return baseUrl ? tryAbsoluteURL(href, baseUrl) : href;\n        })\n        .filter(Boolean) as string[];\n}\n","sourceCodeStart":92,"sourceCodeEnd":119,"githubUrl":"https://github.com/apify/crawlee/blob/dbe57fb09ca607ad59dcf998f3925ef9ac3bb26c/packages/utils/src/internals/cheerio.ts#L92-L119","documentation":"extractUrlsFromCheerio validates every extracted href against the is-absolute-url scheme regex. If a href is relative and no baseUrl option was provided, it throws immediately instead of letting the resulting Request fail later with a confusing error. Pass baseUrl so relative URLs are resolved against it via tryAbsoluteURL.","triggerScenarios":"Calling extractLinks()/urls() on a Cheerio page whose anchors include relative hrefs (e.g. '/about', 'page.html') while omitting the baseUrl option.","commonSituations":"Crawling a site whose HTML uses relative links and forgetting baseUrl in the crawler request options; scraping saved/transformed HTML where the base differs from the source URL; copying an extractLinks call between projects without porting the baseUrl config.","solutions":["Set the baseUrl option in extractLinks()/urls() so relative URLs resolve","Ensure the crawler's Request has a loadedUrl / referer you can pass as baseUrl","Filter out relative hrefs before extraction if you only want absolute URLs","Use the enqueue strategy with a known origin URL instead of raw extraction"],"exampleFix":"// before\nawait extractLinks({ request, page, selector: 'a' });\n\n// after\nawait extractLinks({ request, page, selector: 'a', baseUrl: request.loadedUrl });","handlingStrategy":"validation","validationCode":"function ensureAbsoluteOrProvideBase(href: string, baseUrl?: string): boolean {\n  const isAbsolute = /^[a-z][a-z0-9+.-]*:/.test(href);\n  return isAbsolute || Boolean(baseUrl);\n}","typeGuard":"function hasBaseUrl(o: { baseUrl?: string }): o is { baseUrl: string } {\n  return typeof o.baseUrl === 'string' && o.baseUrl.length > 0;\n}","tryCatchPattern":"try {\n  links = await extractLinks({ request, page, selector: 'a', baseUrl: request.loadedUrl });\n} catch (err) {\n  if (String(err).includes('baseUrl is not set')) links = [];\n  else throw err;\n}","preventionTips":["Always pass baseUrl when crawling pages that may contain relative links","Default baseUrl to request.loadedUrl in your extraction helpers","Sanitize or skip hrefs starting with '/' or '#' when no base is available","Add a test asserting extractLinks options include baseUrl"],"tags":["cheerio","url","scraping"],"backgroundTag":"missing-base-url","analyzedSha":"dbe57fb09ca607ad59dcf998f3925ef9ac3bb26c","analyzedAt":"2026-08-30T22:22:28.328Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}