denoland/deno · error

Unbalanced pattern at {}.

Error message

Unbalanced pattern at {}.

What it means

Final balance check of the pattern lexer at cli/lsp/path_to_regex.rs:182: the '(' that opens a placeholder's custom regex decrements its counter on each matching ')'; if the scan reaches end-of-input with count > 0, the closing paren is missing and the lexer errors with "Unbalanced pattern at {index}" pointing at the opening '('.

Source

Thrown at cli/lsp/path_to_regex.rs:182

              break;
            }
          } else if next_char == Some(&'(') {
            count += 1;
            pattern.push(chars.next().unwrap());
            if chars.peek() != Some(&'?') {
              return Err(anyhow!(
                "Capturing groups are not allowed at {}.",
                index + pattern.len()
              ));
            }
            continue;
          }

          pattern.push(chars.next().unwrap());
        }

        if count > 0 {
          return Err(anyhow!("Unbalanced pattern at {}.", index));
        }
        if pattern.is_empty() {
          return Err(anyhow!("Missing pattern at {}.", index));
        }
        let pattern_len = pattern.len();
        tokens.push(LexToken {
          token_type: TokenType::Pattern,
          index,
          value: pattern,
        });
        index += 2 + pattern_len;
      }
      Some(c) => {
        tokens.push(LexToken {
          token_type: TokenType::Char,
          index,
          value: c.to_string(),
        });

View on GitHub (pinned to 89f33cbef2)

Solutions

  1. Close the pattern: "/x/:pkg([a-z]+)"
  2. Count parens in both directions or lint the schema with a balanced-delimiter check before publishing
  3. Prefer simple character classes over deeply nested groups to reduce the chance of imbalance

Example fix

// before
{ "schema": "/x/:pkg([a-z+" }

// after
{ "schema": "/x/:pkg([a-z]+)" }
Defensive patterns

Strategy: validation

Validate before calling

function isBalanced(s: string): boolean {
  let depth = 0;
  for (let i = 0; i < s.length; i++) {
    const c = s[i];
    if (c === "\\") { i++; continue; }
    if (c === "(") depth++;
    else if (c === ")") depth--;
    if (depth < 0) return false;
  }
  return depth === 0;
}

Prevention

When it happens

Trigger: A schema with an unterminated inline pattern: "/x/:pkg([a-z+" (no closing parenthesis), or nested groups where one ')' was dropped: "/x/:pkg((?:[a-z]+)".

Common situations: Hand-editing long character classes and losing the tail; JSON edits that truncate the schema; diff merges deleting a single ')'.

Related errors


AI-assisted analysis of denoland/deno@89f33cbef2 (2026-08-16). Data as JSON: /api/errors/2dfa2e37952348e5. Report an issue: GitHub.