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
- 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".
- Strip leading dashes/flags from user-supplied query text before passing it to the tool.
- 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
- Strip or escape leading dashes in user/LLM-supplied queries before they reach the search tool.
- Treat this guard firing on untrusted input as a prompt-injection signal worth alerting on.
- Prefer character classes ([-]foo) when searching literal dash-prefixed text.
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
- Access denied - symlink target outside allowed directories.
- Cannot copy symbolic link
- ripgrep exited with code
- @vscode/ripgrep not installed
- Access denied - parent directory outside allowed…
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.dataView on GitHub (pinned to 3aec848f28)