Mintplex-Labs/anything-llm · warning

search pattern must not start with '-'

Error message

search pattern must not start with '-'

What it means

Thrown by searchWithRipgrep when the regex pattern string starts with '-'. Because the pattern is passed as a positional argument to the rg binary, a leading dash could make ripgrep parse it as an option (e.g. --pre=/bin/sh executes commands), so the library rejects it outright even though '--' is also pushed as a separator (defense in depth). This is an intentional argument-injection guard, not a ripgrep limitation.

Solutions

  1. Escape the leading dash so the regex still matches a literal '-': prefix with a backslash, e.g. pattern "\\-flag" or use a character class "[-]flag".
  2. Strip leading dashes/flags from user-supplied query text before passing it to the tool.
  3. Treat hits of this error from untrusted input as a prompt-injection signal and sanitize at the application boundary.

Example fix

// before
search_files({ path: ".", query: "--verbose" })  // throws: must not start with '-'

// after
search_files({ path: ".", query: "\\-\\-verbose" })  // escaped literal dashes
Defensive patterns

Strategy: validation

Validate before calling

function sanitizeSearchPattern(pattern) {
  if (typeof pattern !== "string" || pattern.length === 0) {
    throw new Error("search pattern must be a non-empty string");
  }
  // literal leading dash -> escaped regex form the guard accepts and rg still matches
  return pattern.startsWith("-") ? "\\" + pattern : pattern;
}
const safe = sanitizeSearchPattern(userQuery);

Type guard

/** @returns {boolean} pattern cannot be parsed by rg as an option */
function isSafePattern(p) {
  return typeof p === "string" && p.length > 0 && !p.startsWith("-");
}

Try / catch

try {
  const hits = searchWithRipgrep({ searchPath, pattern });
} catch (e) {
  if (e.message === "search pattern must not start with '-'") {
    pattern = "\\" + pattern; // escape literal dash, retry once
  } else throw e;
}

Prevention

When it happens

Trigger: Calling search_files with a pattern that begins with a dash: "-flag", "--verbose", "-\d+"; an LLM echoes a CLI-style flag from user text into the query field; searching for literal text that happens to start with '-' (e.g. "--- separator comments").

Common situations: User asks the agent to 'search for --help' in a codebase; prompt-injection attempts smuggling rg options like --pre through the search tool; regexes written to match dash-prefixed flags or bullet lists.

Related errors


AI-assisted analysis of Mintplex-Labs/anything-llm@3aec848f28 (2026-08-18). Data as JSON: /api/errors/c53d248558bdf645. Report an issue: GitHub.

Appendix: source

Thrown at server/utils/agents/aibitat/plugins/filesystem/search-files.js:347

  // Build ripgrep arguments
  const args = [
    "--json", // JSON output for structured parsing
    "--line-number", // Include line numbers
    "--no-ignore", // Search all files, even those in .gitignore
    "--max-count",
    String(maxResults),
  ];

  if (!caseSensitive) args.push("--ignore-case");
  if (filePattern) args.push("--glob", filePattern);
  for (const exclude of excludePatterns) args.push("--glob", `!${exclude}`);

  // Security: prevent argument injection attacks where a malicious pattern like
  // "--pre=/bin/sh" could cause ripgrep to execute arbitrary commands.
  // The "--" separator tells ripgrep to treat everything after it as positional
  // arguments, not options. The startsWith("-") check is defense-in-depth.
  if (typeof pattern === "string" && pattern.startsWith("-")) {
    throw new Error("search pattern must not start with '-'");
  }
  args.push("--", pattern, searchPath);
  const result = spawnSync(rgPath, args, {
    encoding: "utf-8",
    maxBuffer: 10 * 1024 * 1024, // 10MB
  });

  // Exit code 1 means no matches (not an error)
  if (result.status > 1) {
    throw new Error(
      result.stderr || `ripgrep exited with code ${result.status}`
    );
  }

  const results = [];
  if (!result.stdout) return results;
  const matches = safeJsonParse(result.stdout, []).filter(
    (m) => m.type === "match" && m.data

View on GitHub (pinned to 3aec848f28)