remix-run/remix · error · ParseError
missing hostname
Error message
missing hostname
What it means
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.
Source
Thrown at packages/route-pattern/src/lib/route-pattern/parse.ts:23
PartPattern,
PartPatternToken,
RoutePattern,
} from '../route-pattern.ts'
import type { Mutable } from '../types/utils.ts'
const IDENTIFIER_RE = /^[a-zA-Z_$][a-zA-Z_$0-9]*/
/**
* Parse a route pattern source string
*
* @param source The pattern source, e.g. `'/users/:id'` or `'https://:tenant.example.com/dashboard?tab'`.
* @returns The parsed pattern.
* @throws {ParseError} When the source is malformed.
*/
export function parsePattern<source extends string>(source: source): RoutePattern<source> {
let spans = split(source)
if (spans.port && !spans.hostname) {
throw new ParseError('missing hostname', source, spans.port[0] - 1)
}
return createRoutePattern<source>({
protocol: parseProtocol(source, spans.protocol),
hostname: parseHostname(source, spans.hostname),
port: spans.port ? source.slice(...spans.port) : null,
pathname: spans.pathname
? parsePart(source, { span: spans.pathname, type: 'pathname' })
: parsePart('', { span: [0, 0], type: 'pathname' }),
search: spans.search ? parseSearch(source.slice(...spans.search)) : new Map(),
})
}
/**
* Parse a single URL part (hostname or pathname).
*
* @private
*/View on GitHub (pinned to 9696913134)
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
Example fix
// before
let pattern = parseRoutePattern('https://:8080/users/:id')
// after
let pattern = parseRoutePattern('https://example.com:8080/users/:id') Defensive patterns
Strategy: validation
Validate before calling
try { parseRoutePattern(source) } catch (e) { /* reject at config load time, before app boots */ } Type guard
function isValidPatternSource(source: string): boolean { try { parseRoutePattern(source); return true } catch { return false } } Try / catch
try { parseRoutePattern(source) } catch (e) { if (e instanceof ParseError && e.message === 'missing hostname') { /* add host or reject */ } } Prevention
- Build patterns with a helper that always includes host when port is set
- Parse-check all patterns in tests
When it happens
Trigger: 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'.
Common situations: 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.
Related errors
AI-assisted analysis of remix-run/remix@9696913134 (2026-08-27).
Data as JSON: /api/errors/2db01d3e74669a47.
Report an issue: GitHub.