remix-run/remix · error · CreateHrefError

invalid-hostname-variable

Error message

invalid-hostname-variable

What it means

When substituting a value into a pattern's hostname, the value contained a `.` or a structurally invalid hostname character (@ : / ? # %). These characters cannot appear unescaped in a hostname without breaking URL structure, so href generation fails with invalid-hostname-variable.

Source

Thrown at packages/route-pattern/src/lib/href.ts:379

  return encodePathnameSegment(value).replaceAll('.', '%2E')
}

/**
 * Keep hostname params from changing URL authority structure when parsed. `@` ends userinfo,
 * `:` starts the port, and `/`, `?`, and `#` start the path, query, and fragment. Hostname
 * variables also reject `.` because dots separate host labels; hostname wildcards allow `.` to
 * span labels intentionally.
 *
 * @see https://url.spec.whatwg.org/#authority-state
 * @see https://url.spec.whatwg.org/#host-parsing
 */
const HOSTNAME_PARAM_STRUCTURAL_CHARS = ['@', ':', '/', '?', '#', '%']

export function validateHostnameVariable(value: unknown): string {
  let serialized = String(value)
  for (let char of serialized) {
    if (char === '.' || isInvalidHostnameParamChar(char)) {
      throw new CreateHrefError({
        type: 'invalid-hostname-variable',
        value: serialized,
        char,
      })
    }
  }
  return serialized
}

export function validateHostnameWildcard(value: unknown): string {
  let serialized = String(value)
  for (let char of serialized) {
    if (isInvalidHostnameParamChar(char)) {
      throw new CreateHrefError({
        type: 'invalid-hostname-wildcard',
        value: serialized,
        char,
      })

View on GitHub (pinned to 9696913134)

Solutions

  1. Pass a single DNS label (no dots) for hostname params
  2. Use a `*` wildcard token if multiple labels must be matched/substituted
  3. Sanitize/validate user input (strip or reject @ : / ? # % and .) before building the href

Example fix

// before
href(parseRoutePattern('https://:tenant.app.com/dashboard'), { tenant: 'acme.eu' })

// after
href(parseRoutePattern('https://:tenant.app.com/dashboard'), { tenant: 'acme' })
Defensive patterns

Strategy: type-guard

Validate before calling

const INVALID = new Set(['@', ':', '/', '?', '#', '%'])
function isValidHostLabel(v: string) { return ![...v].some(c => c === '.' || INVALID.has(c)) }

Type guard

function isHostnameLabel(value: unknown): value is string {
  return typeof value === 'string' && value.length > 0 && ![...value].some(c => c === '.' || '@:/?#%'.includes(c))
}

Prevention

When it happens

Trigger: Calling href on a pattern with a hostname param (e.g. `:subdomain.example.com`) with a value containing a dot or any of @ : / ? # %, such as 'my.sub' or 'user@host'.

Common situations: Treating hostname wildcards/params as free-text; interpolating user-typed input or email addresses into subdomain params; multi-label domains passed where a single label is expected.

Related errors


AI-assisted analysis of remix-run/remix@9696913134 (2026-08-27). Data as JSON: /api/errors/27a72f74d7e78f25. Report an issue: GitHub.