crewAIInc/crewAI · error · ValueError
action='replace' requires 'paths' (list of file paths).
Error message
action='replace' requires 'paths' (list of file paths).
What it means
DaytonaFileToolSchema._validate_action_args handles action='replace' (bulk find-and-replace across multiple files) specially: it requires 'paths', a list of file paths to operate on. Unlike single-file actions, replace works on a list, so a missing or empty paths raises this ValueError.
Source
Thrown at lib/crewai-tools/src/crewai_tools/tools/daytona_sandbox_tool/daytona_file_tool.py:208
raise ValueError(
"action='append' requires 'content'. Pass the chunk to append "
"in the 'content' field."
)
if self.action == "move" and not self.destination:
raise ValueError("action='move' requires 'destination'.")
if self.action == "find" and not self.pattern:
raise ValueError(
"action='find' requires 'pattern' (text to search for inside files)."
)
if self.action == "search" and not self.pattern:
raise ValueError("action='search' requires 'pattern' (glob, e.g. '*.py').")
if self.action == "chmod" and not (self.mode or self.owner or self.group):
raise ValueError(
"action='chmod' requires at least one of 'mode', 'owner', or 'group'."
)
if self.action == "replace":
if not self.paths:
raise ValueError(
"action='replace' requires 'paths' (list of file paths)."
)
if not self.pattern:
raise ValueError("action='replace' requires 'pattern'.")
if self.replacement is None:
raise ValueError("action='replace' requires 'replacement'.")
return self
class DaytonaFileTool(DaytonaBaseTool):
"""Read, write, and manage files inside a Daytona sandbox.
Notes:
- Most useful with `persistent=True` or an explicit `sandbox_id`. With the
default ephemeral mode, files disappear when this tool call finishes.
"""
name: str = "Daytona Sandbox Files"View on GitHub (pinned to 754d7323be)
Solutions
- Use the list field: tool.run(action='replace', paths=['/workspace/a.py','/workspace/b.py'], pattern='foo', replacement='bar').
- If the list is produced dynamically, assert it is non-empty before calling.
- Remember replace ignores 'path' entirely — only 'paths' counts.
Example fix
# before tool.run(action="replace", path="/workspace/a.py", pattern="foo", replacement="bar") # ValueError: paths missing # after tool.run(action="replace", paths=["/workspace/a.py"], pattern="foo", replacement="bar")
Defensive patterns
Strategy: validation
Validate before calling
def validate_replace(paths: list[str] | None, pattern: str | None, replacement: str | None) -> None:
if not paths:
raise ValueError("replace requires non-empty 'paths' list")
if not pattern:
raise ValueError("replace requires 'pattern'")
if replacement is None:
raise ValueError("replace requires explicit 'replacement' (may be '')")
validate_replace(paths, pattern, replacement) Try / catch
try:
tool.run(action="replace", paths=ps, pattern=pat, replacement=rep)
except ValidationError as e:
if "requires 'paths'" in str(e):
ps = [p] if isinstance(p, str) else list(ps or [])
tool.run(action="replace", paths=ps, pattern=pat, replacement=rep)
else:
raise Prevention
- replace uses the plural 'paths' list — never 'path'.
- Assert the path list is non-empty after any globbing step.
- Prefer a bulk-edit helper that always fills paths, pattern, and replacement together.
When it happens
Trigger: Calling DaytonaFileTool with action='replace', pattern and replacement set, but paths omitted/empty; passing a single string in 'path' instead of the list field 'paths'.
Common situations: Caller mixes up path (single file) and paths (list) for the replace action; agent emits paths=None; upstream glob returned an empty list.
Related errors
- action={self.action!r} requires 'path'.
- action='append' requires 'content'. Pass the chunk to append
- action='move' requires 'destination'.
- action='replace' requires 'pattern'.
- action='replace' requires 'replacement'.
AI-assisted analysis of crewAIInc/crewAI@754d7323be (2026-08-15).
Data as JSON: /api/errors/1e9249f05b516f1e.
Report an issue: GitHub.