{"record":{"id":"cacaf51b33543511","repo":"remix-run/remix","slug":"invalid-origin-json-stringify-origin","errorCode":null,"errorMessage":"invalid origin ${JSON.stringify(origin)}","messagePattern":"invalid origin (.+?)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/cop-middleware/src/lib/cop.ts","lineNumber":186,"sourceCode":"  }\n\n  let trimmedValue = value.trim()\n  return trimmedValue === '' ? null : trimmedValue\n}\n\nfunction validateTrustedOrigin(origin: string): string {\n  let trimmedOrigin = origin.trim()\n  if (trimmedOrigin === '') {\n    throw new Error('trusted origin must not be empty')\n  }\n\n  if (trimmedOrigin.endsWith('/')) {\n    throw new Error(`invalid origin ${JSON.stringify(origin)}: trailing slash is not allowed`)\n  }\n\n  let parsedOrigin = parseOrigin(trimmedOrigin)\n  if (parsedOrigin == null) {\n    throw new Error(`invalid origin ${JSON.stringify(origin)}`)\n  }\n\n  if (parsedOrigin.pathname !== '/' || parsedOrigin.search !== '' || parsedOrigin.hash !== '') {\n    throw new Error(\n      `invalid origin ${JSON.stringify(origin)}: path, query, and fragment are not allowed`,\n    )\n  }\n\n  return serializeOrigin(parsedOrigin)\n}\n\nfunction normalizeOrigin(origin: string): string | null {\n  let parsedOrigin = parseOrigin(origin)\n  return parsedOrigin == null ? null : serializeOrigin(parsedOrigin)\n}\n\nfunction parseOrigin(origin: string): URL | null {\n  try {","sourceCodeStart":168,"sourceCodeEnd":204,"githubUrl":"https://github.com/remix-run/remix/blob/9696913134be3a4423513d2775f7b31d6917c049/packages/cop-middleware/src/lib/cop.ts#L168-L204","documentation":"The value must parse as a URL with a valid origin (scheme + host). If parseOrigin returns null the string is not a usable origin at all, so trusting it is impossible.","triggerScenarios":"addTrustedOrigin with values like 'example.com' (no scheme), 'localhost:3000', 'ftp://x', or random text.","commonSituations":"Omitting the protocol because browsers display URLs without it; using host:port strings from PORT env vars; typos in config files.","solutions":["Always include the scheme: 'https://example.com' or 'http://localhost:3000'","For local dev use http://localhost:PORT explicitly","Validate with `new URL(value)` in a config check before passing to the middleware"],"exampleFix":"// before\ncop.addTrustedOrigin('api.example.com')\n// after\ncop.addTrustedOrigin('https://api.example.com')","handlingStrategy":"type-guard","validationCode":"function isOrigin(v: string) { try { return new URL(v).origin !== 'null' } catch { return false } }\nif (!isOrigin(origin)) throw new Error('bad origin config')","typeGuard":"function isValidOrigin(v: unknown): v is string {\n  if (typeof v !== 'string') return false\n  try { return new URL(v).origin !== 'null' } catch { return false }\n}","tryCatchPattern":null,"preventionTips":["Always include the scheme","Use http://localhost:3000 for local dev","Lint config for missing protocols"],"tags":["cop-middleware","security","origin","url-parsing"],"backgroundTag":"invalid-origin-config","analyzedSha":"9696913134be3a4423513d2775f7b31d6917c049","analyzedAt":"2026-08-27T19:55:01.024Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}