remix-run/remix · error · ParseError

missing variable name

Error message

missing variable name

What it means

Pattern syntax uses `:name` for variable capture. A ':' was encountered but no valid identifier follows it before the end of the span, so there is no name to bind the capture to. parsePart throws 'missing variable name' pointing at the colon.

Source

Thrown at packages/route-pattern/src/lib/route-pattern/parse.ts:96

      let optional = optionalStack.pop()
      if (optional === undefined) {
        throw new ParseError('unmatched )', source, i)
      }
      if (optional.tokenIndex === tokens.length - 1) {
        emptyOptionals.push(optional.sourceIndex)
      }
      optionals.set(optional.tokenIndex, tokens.length)
      tokens.push({ type: char })
      tokenIndices.push(i)
      i += 1
      continue
    }

    if (char === ':') {
      i += 1
      let name = IDENTIFIER_RE.exec(source.slice(i, span[1]))?.[0]
      if (!name) {
        throw new ParseError('missing variable name', source, i - 1)
      }
      tokens.push({ type: ':', name })
      tokenIndices.push(i - 1)
      i += name.length
      continue
    }

    if (char === '*') {
      i += 1
      let name = IDENTIFIER_RE.exec(source.slice(i, span[1]))?.[0]
      tokens.push({ type: '*', name: name ?? '*' })
      tokenIndices.push(i - 1)
      i += name?.length ?? 0
      continue
    }

    if (char === separator) {
      tokens.push({ type: 'separator' })

View on GitHub (pinned to 9696913134)

Solutions

  1. Give the variable a name: '/users/:id'
  2. Escape colons that are literal: '/schedule/\:time' (see the escape handling in the same parser)
  3. Check the error position — it points at the offending ':'

Example fix

// before
let pattern = parseRoutePattern('/users/:')

// after
let pattern = parseRoutePattern('/users/:id')
Defensive patterns

Strategy: validation

Validate before calling

let colonNames = [...source.matchAll(/:(\w*)/g)].filter(m => !m[1])
if (colonNames.length) throw new Error('Colon without a variable name')

Type guard

null

Try / catch

try { parseRoutePattern(source) } catch (e) { if (e instanceof ParseError && e.message === 'missing variable name') { /* fix at e.position */ } }

Prevention

When it happens

Trigger: Calling parseRoutePattern with a pattern like '/users/:' , '/:' , or '/files/:.json' where ':' is followed by a non-identifier character or nothing. Only IDENTIFIER_RE-compatible names are accepted.

Common situations: Truncating a pattern during refactor (':id' reduced to ':'); a colon intended literally in a path (e.g. time '12:30') left unescaped; templating bug leaving an empty placeholder '{{user}}' removed but ':' kept.

Related errors


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