ruvnet/ruflo · error

Pattern rejected: nested quantifiers detected (potential ReD

Error message

Pattern rejected: nested quantifiers detected (potential ReDoS): ${pattern}

What it means

MemoryAuthority.addPattern() compiles caller-supplied regexes into irreversibility classifications, so it screens them for catastrophic-backtracking constructs first. Nested quantifiers such as `(a+)+`, `(a*)*`, or `(\w+){2,}*` can turn an adversarial subject string into exponential CPU time (ReDoS), and because these patterns run over memory-change descriptions they are rejected before `new RegExp` ever runs. The heuristic test itself is a regex over the pattern text.

Source

Thrown at v3/@claude-flow/guidance/src/authority.ts:672

        return this.costlyReversiblePatterns.map(p => p.source);
      case 'reversible':
        return this.reversiblePatterns.map(p => p.source);
    }
  }

  /**
   * Add a pattern to a classification.
   *
   * Validates the pattern against ReDoS heuristics before accepting it.
   * Rejects patterns with nested quantifiers (e.g., `(a+)+`) that can
   * cause catastrophic backtracking.
   *
   * @throws Error if the pattern is invalid regex or contains ReDoS-prone constructs
   */
  addPattern(classification: IrreversibilityClass, pattern: string): void {
    // ReDoS heuristic: reject nested quantifiers like (a+)+, (a*)+, (a+)*, etc.
    if (/([+*]|\{[0-9]+,?\})\s*\)[\s]*[+*]|\{[0-9]+,?\}/.test(pattern)) {
      throw new Error(`Pattern rejected: nested quantifiers detected (potential ReDoS): ${pattern}`);
    }
    // Also reject patterns longer than 500 chars as a sanity bound
    if (pattern.length > 500) {
      throw new Error(`Pattern rejected: exceeds maximum length of 500 characters`);
    }

    const regex = new RegExp(pattern, 'i');

    switch (classification) {
      case 'irreversible':
        this.irreversiblePatterns.push(regex);
        break;
      case 'costly-reversible':
        this.costlyReversiblePatterns.push(regex);
        break;
      case 'reversible':
        this.reversiblePatterns.push(regex);
        break;

View on GitHub (pinned to fa13ee4ad6)

Solutions

  1. Flatten the pattern: `(a+)+` → `a+`; `(\w+)*` → `\w*`
  2. Use a single bounded repetition: `(?:ab){1,10}` instead of `(ab+)+`
  3. Validate patterns against the same heuristic in your test suite before shipping them to production
  4. If the construct is unavoidable, pre-filter input length so backtracking is bounded

Example fix

// before
authority.addPattern('irreversible', '(a+)+b'); // throws ReDoS rejection

// after
authority.addPattern('irreversible', 'a+b'); // no nested quantifier
Defensive patterns

Strategy: validation

Validate before calling

const nestedQuantifier =
  /([+*]|\{[0-9]+,?\})\s*\)[\s]*[+*]|\{[0-9]+,?\}/;
if (nestedQuantifier.test(pattern)) {
  throw new Error(`pattern contains nested quantifiers: ${pattern}`);
}
authority.addPattern(classification, pattern);

Prevention

When it happens

Trigger: addPattern('irreversible', '(a+)+'); any pattern where a quantifier (+, *, or {n,}) immediately follows a group that itself contains a quantifier, e.g. '(\\w+)*', '(.|a)+*'; feeding unvetted patterns sourced from user input or LLM output.

Common situations: Auto-generating classification patterns from logs or model output; copy-pasting a regex from an answer site that uses nested quantifiers; converting a glob or fuzzy matcher to regex and accidentally nesting repetition.

Related errors


AI-assisted analysis of ruvnet/ruflo@fa13ee4ad6 (2026-08-18). Data as JSON: /api/errors/4b2b4bcea2c061a1. Report an issue: GitHub.