{"record":{"id":"5d64d5d5cfcbfb40","repo":"can1357/oh-my-pi","slug":"invalid-firecrawl-base-url-expected-an-http-or-ht","errorCode":null,"errorMessage":"Invalid Firecrawl base URL: expected an HTTP or HTTPS URL","messagePattern":"Invalid Firecrawl base URL: expected an HTTP or HTTPS URL","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/web/search/providers/firecrawl.ts","lineNumber":40,"sourceCode":"\nconst FIRECRAWL_DEFAULT_BASE_URL = \"https://api.firecrawl.dev/v2\";\nconst DEFAULT_NUM_RESULTS = 10;\nconst MAX_NUM_RESULTS = 100;\n\nconst RECENCY_TBS: Record<NonNullable<SearchParams[\"recency\"]>, string> = {\n\tday: \"qdr:d\",\n\tweek: \"qdr:w\",\n\tmonth: \"qdr:m\",\n\tyear: \"qdr:y\",\n};\nfunction resolveSearchUrl(): string {\n\tconst configured = process.env.FIRECRAWL_BASE_URL ?? process.env.FIRECRAWL_API_URL;\n\tif (!configured?.trim()) return `${FIRECRAWL_DEFAULT_BASE_URL}/search`;\n\tlet url: URL;\n\ttry {\n\t\turl = new URL(configured.trim());\n\t} catch {\n\t\tthrow new Error(\"Invalid Firecrawl base URL: expected an HTTP or HTTPS URL\");\n\t}\n\tif (url.protocol !== \"http:\" && url.protocol !== \"https:\") {\n\t\tthrow new Error(\"Invalid Firecrawl base URL: expected an HTTP or HTTPS URL\");\n\t}\n\tif (url.username || url.password) {\n\t\tthrow new Error(\"Invalid Firecrawl base URL: URL credentials are not allowed\");\n\t}\n\turl.search = \"\";\n\turl.hash = \"\";\n\turl.pathname = url.pathname.replace(/\\/+$/, \"\");\n\tif (!/\\/v[12]$/i.test(url.pathname)) url.pathname += \"/v2\";\n\turl.pathname += \"/search\";\n\treturn url.toString();\n}\n\nexport interface FirecrawlSearchParams {\n\tquery: string;\n\tnum_results?: number;","sourceCodeStart":22,"sourceCodeEnd":58,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/web/search/providers/firecrawl.ts#L22-L58","documentation":"resolveSearchUrl() validates the Firecrawl base URL configured via FIRECRAWL_BASE_URL or FIRECRAWL_API_URL before building the /search endpoint. This variant is thrown when the configured value cannot be parsed by `new URL()` at all — it is not a syntactically valid absolute URL. The provider refuses to guess a base and fails fast so requests never go to a malformed endpoint.","triggerScenarios":"FIRECRAWL_BASE_URL or FIRECRAWL_API_URL is set to something `new URL(value.trim())` rejects: a bare hostname like 'localhost:3002' with no scheme is fine but 'localhost' alone, 'my-firecrawl.internal' (no protocol), a value with stray spaces/tabs that survive after trim, or an empty-ish value that passed the trim check but is unparsable.","commonSituations":"Self-hosted Firecrawl users setting FIRECRAWL_BASE_URL=localhost:3002 (forgot http://), paste errors adding quotes or whitespace into env files, or a deploy system injecting a placeholder value like 'CHANGE_ME'.","solutions":["Set FIRECRAWL_BASE_URL to a fully-qualified absolute URL including the scheme, e.g. http://localhost:3002 or https://api.firecrawl.dev","If you meant to use the hosted API, unset both FIRECRAWL_BASE_URL and FIRECRAWL_API_URL so the default https://api.firecrawl.dev is used","Check .env / shell export for stray quotes, trailing whitespace, or line-continuation characters corrupting the value"],"exampleFix":"// before\nFIRECRAWL_BASE_URL=localhost:3002\n// after\nFIRECRAWL_BASE_URL=http://localhost:3002","handlingStrategy":"validation","validationCode":"function isValidFirecrawlBaseUrl() {\n  const v = process.env.FIRECRAWL_BASE_URL ?? process.env.FIRECRAWL_API_URL;\n  if (!v?.trim()) return true; // default applies\n  try {\n    const u = new URL(v.trim());\n    return u.protocol === \"http:\" || u.protocol === \"https:\";\n  } catch {\n    return false;\n  }\n}\nif (!isValidFirecrawlBaseUrl()) throw new Error(\"Fix FIRECRAWL_BASE_URL: must be an absolute http(s) URL\");","typeGuard":"function isHttpUrl(value: string): value is string {\n  try {\n    const u = new URL(value);\n    return u.protocol === \"http:\" || u.protocol === \"https:\";\n  } catch {\n    return false;\n  }\n}","tryCatchPattern":"try {\n  await firecrawlSearch(query);\n} catch (err) {\n  if (err instanceof Error && err.message.includes(\"Invalid Firecrawl base URL\")) {\n    logger.error(\"Firecrawl configuration invalid — check FIRECRAWL_BASE_URL\", { cause: err.message });\n    // fall back to default provider or abort startup\n  } else {\n    throw err;\n  }\n}","preventionTips":["Always include the scheme (http:// or https://) in FIRECRAWL_BASE_URL / FIRECRAWL_API_URL values","Validate env vars at startup with a URL-parse check before the app accepts traffic","Keep .env values unquoted and free of trailing whitespace","Use the hosted API default by leaving the vars unset when you don't need a custom endpoint"],"tags":["configuration","url-validation","env-var"],"backgroundTag":"invalid-base-url","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}