remix-run/remix · error · ParseError
adjacent wildcards
Error message
adjacent wildcards
What it means
During grammar validation after tokenizing, two wildcard (`*`) tokens were found adjacent to each other with no separator between them (e.g. '/a/**/b' as two splats back-to-back). Adjacent wildcards are ambiguous to match and serialize, so a ParseError 'adjacent wildcards' is thrown at the second wildcard's index.
Source
Thrown at packages/route-pattern/src/lib/route-pattern/parse.ts:162
return { tokens, optionals, type: options.type }
}
function validateTokenGrammar(
source: string,
tokens: ReadonlyArray<PartPatternToken>,
optionals: ReadonlyMap<number, number>,
tokenIndices: ReadonlyArray<number>,
): void {
for (let [index, token] of tokens.entries()) {
if (token.type !== ':' && token.type !== '*') continue
for (let next of nextConsumingTokenIndices(tokens, optionals, index + 1)) {
if (next === tokens.length) continue
let nextToken = tokens[next]
if (token.type === '*') {
if (nextToken.type === '*') {
throw new ParseError('adjacent wildcards', source, tokenIndices[next])
}
continue
}
if (
nextToken.type === 'separator' ||
nextToken.type === '*' ||
(nextToken.type === 'text' && nextToken.text.startsWith('.'))
) {
continue
}
throw new ParseError('invalid param delimiter', source, tokenIndices[next])
}
}
}
function nextConsumingTokenIndices(
tokens: ReadonlyArray<PartPatternToken>,View on GitHub (pinned to 9696913134)
Solutions
- Use a single wildcard: '/files/*/download' or '/files/*'
- If you need multi-segment matching, one splat already spans separators — use a named splat '*rest'
- Insert a literal or named param between the wildcards if both are genuinely needed
Example fix
// before
let pattern = parseRoutePattern('/files/*/*/download')
// after
let pattern = parseRoutePattern('/files/*rest/download') Defensive patterns
Strategy: validation
Validate before calling
if (/\*(\([^)]*\))?\*/.test(source.replace(/\\./g, ''))) throw new Error('Adjacent wildcards') Type guard
null
Try / catch
try { parseRoutePattern(source) } catch (e) { if (e instanceof ParseError && e.message === 'adjacent wildcards') { /* collapse to one splat */ } } Prevention
- One splat per region; use named splats
- Don't copy glob '**' syntax
When it happens
Trigger: Calling parseRoutePattern with a pattern containing two splat tokens next to each other or separated only by optionals that can collapse, such as '/files/*/*/download' or '/a/*/b(*)/c' where the optional resolves to adjacent wildcards.
Common situations: Copying glob syntax (`**`) from file matchers into route patterns; concatenating two catch-all fragments; refactoring nested routes into one pattern and stacking splats.
Related errors
AI-assisted analysis of remix-run/remix@9696913134 (2026-08-27).
Data as JSON: /api/errors/993af1138209b409.
Report an issue: GitHub.