{"record":{"id":"2db01d3e74669a47","repo":"remix-run/remix","slug":"missing-hostname-2db01d","errorCode":null,"errorMessage":"missing hostname","messagePattern":"missing hostname","errorType":"validation","errorClass":"ParseError","httpStatus":null,"severity":"error","filePath":"packages/route-pattern/src/lib/route-pattern/parse.ts","lineNumber":23,"sourceCode":"  PartPattern,\n  PartPatternToken,\n  RoutePattern,\n} from '../route-pattern.ts'\nimport type { Mutable } from '../types/utils.ts'\n\nconst IDENTIFIER_RE = /^[a-zA-Z_$][a-zA-Z_$0-9]*/\n\n/**\n * Parse a route pattern source string\n *\n * @param source The pattern source, e.g. `'/users/:id'` or `'https://:tenant.example.com/dashboard?tab'`.\n * @returns The parsed pattern.\n * @throws {ParseError} When the source is malformed.\n */\nexport function parsePattern<source extends string>(source: source): RoutePattern<source> {\n  let spans = split(source)\n  if (spans.port && !spans.hostname) {\n    throw new ParseError('missing hostname', source, spans.port[0] - 1)\n  }\n\n  return createRoutePattern<source>({\n    protocol: parseProtocol(source, spans.protocol),\n    hostname: parseHostname(source, spans.hostname),\n    port: spans.port ? source.slice(...spans.port) : null,\n    pathname: spans.pathname\n      ? parsePart(source, { span: spans.pathname, type: 'pathname' })\n      : parsePart('', { span: [0, 0], type: 'pathname' }),\n    search: spans.search ? parseSearch(source.slice(...spans.search)) : new Map(),\n  })\n}\n\n/**\n * Parse a single URL part (hostname or pathname).\n *\n * @private\n */","sourceCodeStart":5,"sourceCodeEnd":41,"githubUrl":"https://github.com/remix-run/remix/blob/9696913134be3a4423513d2775f7b31d6917c049/packages/route-pattern/src/lib/route-pattern/parse.ts#L5-L41","documentation":"parsePattern splits a pattern source into protocol/hostname/port/pathname spans. If the pattern declares a port but no hostname (e.g. 'https://:8080/x'), there is nothing to attach the port to, which is structurally invalid per the URL spec. A ParseError with code 'missing hostname' is thrown pointing at the character just before the port span.","triggerScenarios":"Calling parseRoutePattern (or APIs that parse, like `pattern`/`partsOf`/matcher insert) with a source containing `:port` but no host, such as ':8080/x' or 'https://:8080/x'.","commonSituations":"Building pattern strings by template concatenation and omitting the host variable; converting relative URL patterns to absolute ones incompletely; config-driven patterns where the host placeholder was accidentally empty.","solutions":["Add a hostname before the port: 'https://example.com:8080/x'","Use a wildcard host if any host is acceptable: 'https://*:8080/x'","Drop the port if it's not needed","Validate pattern sources with parseRoutePattern in a unit test before registering them"],"exampleFix":"// before\nlet pattern = parseRoutePattern('https://:8080/users/:id')\n\n// after\nlet pattern = parseRoutePattern('https://example.com:8080/users/:id')","handlingStrategy":"validation","validationCode":"try { parseRoutePattern(source) } catch (e) { /* reject at config load time, before app boots */ }","typeGuard":"function isValidPatternSource(source: string): boolean { try { parseRoutePattern(source); return true } catch { return false } }","tryCatchPattern":"try { parseRoutePattern(source) } catch (e) { if (e instanceof ParseError && e.message === 'missing hostname') { /* add host or reject */ } }","preventionTips":["Build patterns with a helper that always includes host when port is set","Parse-check all patterns in tests"],"tags":["route-pattern","parse","url","hostname"],"backgroundTag":"invalid-url-pattern","analyzedSha":"9696913134be3a4423513d2775f7b31d6917c049","analyzedAt":"2026-08-27T19:55:01.024Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}