{"record":{"id":"0993cdddfdcb5cf6","repo":"mastra-ai/mastra","slug":"regexfilterprocessor-requires-at-least-one-rule-or","errorCode":null,"errorMessage":"RegexFilterProcessor requires at least one rule or preset","messagePattern":"RegexFilterProcessor requires at least one rule or preset","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/core/src/processors/processors/regex-filter.ts","lineNumber":323,"sourceCode":"  private phase: 'input' | 'output' | 'all';\n  private includeRedactedValues: boolean;\n  private streamCarryoverSize: number;\n\n  /**\n   * Invoked when the `redact` strategy rewrites a piece of text, once per\n   * redacted message, message part, or stream chunk, with a\n   * {@link RegexRedactionDetail} as `detail`. The `block` strategy reports\n   * through the same callback, driven by the processor runner when it catches\n   * the TripWire.\n   */\n  public onViolation?: (violation: ProcessorViolation) => void | Promise<void>;\n\n  constructor(options: RegexFilterOptions) {\n    const presetRules = (options.presets ?? []).flatMap(preset => PRESET_MAP[preset] ?? []);\n    this.rules = [...presetRules, ...(options.rules ?? [])];\n\n    if (this.rules.length === 0) {\n      throw new Error('RegexFilterProcessor requires at least one rule or preset');\n    }\n\n    this.strategy = options.strategy ?? 'block';\n    this.phase = options.phase ?? 'all';\n    this.includeRedactedValues = options.includeRedactedValues ?? false;\n    this.streamCarryoverSize = options.streamCarryoverSize ?? STREAM_CARRYOVER_SIZE;\n    if (!Number.isSafeInteger(this.streamCarryoverSize) || this.streamCarryoverSize < 1) {\n      throw new Error('RegexFilterProcessor streamCarryoverSize must be a positive safe integer');\n    }\n  }\n\n  /**\n   * Run every rule over the text and collect all matches, grouped by rule in\n   * declaration order. Matches may overlap; callers that rewrite text must\n   * de-overlap them first.\n   */\n  private collectMatches(text: string): RuleMatch[] {\n    const matches: RuleMatch[] = [];","sourceCodeStart":305,"sourceCodeEnd":341,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/packages/core/src/processors/processors/regex-filter.ts#L305-L341","documentation":"RegexFilterProcessor applies regex-based filtering rules (with optional named presets) to text and streams. The constructor requires at least one rule, either via `rules` or via named `presets`; with none it would be a no-op processor, so it throws immediately at construction time. This fail-fast check prevents silently configuring a filter that does nothing.","triggerScenarios":"Constructing `new RegexFilterProcessor({})`, or passing `{ presets: [] }` and/or `{ rules: [] }` such that the merged rule array (preset rules plus custom rules) is empty. Also occurs when preset names are misspelled: `PRESET_MAP[preset] ?? []` silently yields no rules for unknown preset strings.","commonSituations":"Typos in preset names (e.g. `'emails'` instead of `'email'`), reading options from env/config that resolves to empty arrays, copying an example but removing the rules array, or building options dynamically so all rules get filtered out before construction.","solutions":["Add at least one rule to `options.rules` (e.g. `{ pattern: /secret/i, replacement: '[REDACTED]' }`).","Pass one or more valid preset names in `options.presets` (verify spelling against PRESET_MAP exports).","Log/inspect the merged options object before construction to confirm rules or presets are non-empty.","If rules are optional in your app, conditionally skip adding the processor instead of constructing it with none."],"exampleFix":"// before\nnew RegexFilterProcessor({ presets: ['emailz'] });\n// after\nnew RegexFilterProcessor({ presets: ['email'] });\n// or\nnew RegexFilterProcessor({ rules: [{ pattern: /api[-_]?key/i, replacement: '[REDACTED]' }] });","handlingStrategy":"validation","validationCode":"function assertRegexFilterOptions(o) {\n  const presets = (o.presets ?? []).flatMap(p => PRESET_MAP[p] ?? []);\n  if (presets.length + (o.rules?.length ?? 0) === 0) throw new TypeError('RegexFilterProcessor needs at least one rule or a valid preset');\n}","typeGuard":"function hasRules(o): o is RegexFilterOptions & { rules: NonNullable<RegexFilterOptions['rules']> } {\n  return Array.isArray(o.rules) && o.rules.length > 0;\n}","tryCatchPattern":"try {\n  const p = new RegexFilterProcessor(opts);\n} catch (e) {\n  if (e.message.includes('at least one rule or preset')) {\n    console.error('Invalid filter config, using default rules', opts);\n    p = new RegexFilterProcessor({ rules: DEFAULT_RULES });\n  } else throw e;\n}","preventionTips":["Never construct the processor from unvalidated config; assert rules/presets non-empty first.","Validate preset names against the exported preset list to catch typos (unknown presets are silently ignored).","Add a unit test that constructs all processors from your production config."],"tags":["configuration","validation","processors","constructor"],"backgroundTag":"empty-processor-configuration","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}