{"record":{"id":"464a78dc1cb622f4","repo":"medusajs/medusa","slug":"oidc-label-must-be-a-valid-url","errorCode":null,"errorMessage":"OIDC '${label}' must be a valid URL","messagePattern":"OIDC '(.+?)' must be a valid URL","errorType":"validation","errorClass":"MedusaError","httpStatus":400,"severity":"error","filePath":"packages/modules/providers/auth-oidc/src/utils/assert-secure-url.ts","lineNumber":12,"sourceCode":"import { isProduction, MedusaError } from \"@medusajs/framework/utils\"\n\n/**\n * Asserts that a URL is `https`. Outside of production, `http` is allowed for\n * localhost so local development and testing remain possible.\n */\nexport const assertSecureUrl = (value: string, label: string): void => {\n  let url: URL\n  try {\n    url = new URL(value)\n  } catch (e) {\n    throw new MedusaError(\n      MedusaError.Types.INVALID_DATA,\n      `OIDC '${label}' must be a valid URL`\n    )\n  }\n\n  const isLocalhost =\n    url.hostname === \"localhost\" ||\n    url.hostname === \"127.0.0.1\" ||\n    url.hostname === \"::1\" ||\n    url.hostname === \"[::1]\"\n\n  const allowsHttp = isLocalhost && !isProduction()\n\n  if (url.protocol !== \"https:\" && !(url.protocol === \"http:\" && allowsHttp)) {\n    throw new MedusaError(\n      MedusaError.Types.INVALID_DATA,\n      `OIDC '${label}' must use https (http is only allowed for localhost outside of production)`\n    )","sourceCodeStart":1,"sourceCodeEnd":30,"githubUrl":"https://github.com/medusajs/medusa/blob/5e06e544a296b9033f20f71f11c559f81a0e5739/packages/modules/providers/auth-oidc/src/utils/assert-secure-url.ts#L1-L30","documentation":"assertSecureUrl is used to validate the issuer and callback_url options. If new URL(value) throws (malformed string, empty, missing scheme), it raises INVALID_DATA saying the '<label>' must be a valid URL.","triggerScenarios":"Passing issuer: \"auth.example.com\" (no scheme), issuer: \"\" , or issuer: undefined-as-string; a callback_url with unencoded spaces or a stray trailing character.","commonSituations":"Missing https:// prefix — the single most common cause; env var containing quotes or whitespace; concatenation bugs producing strings like \"https://example.com/none\" + undefined; copy-pasting the well-known path into issuer with a typo.","solutions":["Prefix the scheme: issuer must look like https://auth.example.com (issuer is the base URL, no /.well-known/... path needed).","Trim and sanitize env values; check for stray quotes from .env formatting.","Log the resolved value at startup to catch undefined interpolation before it reaches the provider."],"exampleFix":"// before\noptions: { issuer: \"auth.example.com\", ... }\n// after\noptions: { issuer: \"https://auth.example.com\", ... }","handlingStrategy":"validation","validationCode":"function isValidUrl(value: string): boolean {\n  try { new URL(value); return true } catch { return false }\n}\nif (!isValidUrl(options.issuer)) throw new Error(\"issuer must be a valid URL including scheme\")","typeGuard":"const isValidHttpUrl = (v: string): v is \\`https://\\${string}\\` => { try { const u = new URL(v); return u.protocol === \"https:\" || u.protocol === \"http:\" } catch { return false } }","tryCatchPattern":"try { assertSecureUrl(value, \"issuer\") } catch (e) { if (e instanceof MedusaError && /must be a valid URL/.test(e.message)) { /* fix the string (add https://) */ } throw e }","preventionTips":["Always include the https:// scheme in issuer and callback_url.","Trim env values and strip stray quotes.","Log resolved URLs at startup to catch interpolation bugs."],"tags":["oidc","url-validation","configuration","startup"],"backgroundTag":"invalid-url-config","analyzedSha":"5e06e544a296b9033f20f71f11c559f81a0e5739","analyzedAt":"2026-08-27T07:24:39.599Z","schemaVersion":2},"datasetVersion":"2026-08-27T08:17:20.692Z"}