crewAIInc/crewAI · error · ValueError

action='replace' requires 'replacement'.

Error message

action='replace' requires 'replacement'.

What it means

The final check in the action='replace' branch of DaytonaFileToolSchema._validate_action_args requires 'replacement' (the new text). replacement is Optional[str] with a None default, so the validator explicitly rejects its absence — you may pass an empty string (to delete matches) but not omit the field.

Source

Thrown at lib/crewai-tools/src/crewai_tools/tools/daytona_sandbox_tool/daytona_file_tool.py:214

        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"
    description: str = (
        "Perform filesystem operations inside a Daytona sandbox: read, "
        "write, append, list, delete, mkdir, info, exists, move, find "
        "(content grep), search (filename glob), chmod (permissions/owner/"
        "group), and replace (bulk find-and-replace across files). "
        "For files larger than a few KB, create the file with action='write' "

View on GitHub (pinned to 754d7323be)

Solutions

  1. Always supply replacement: tool.run(action='replace', paths=[...], pattern='x', replacement='y').
  2. To delete all occurrences, pass replacement='' explicitly.
  3. If you only want to locate text, use action='find' instead.

Example fix

# before
tool.run(action="replace", paths=["/workspace/a.py"], pattern="foo")  # ValueError

# after
tool.run(action="replace", paths=["/workspace/a.py"], pattern="foo", replacement="foo2")
# or to delete matches:
tool.run(action="replace", paths=["/workspace/a.py"], pattern="foo", replacement="")
Defensive patterns

Strategy: validation

Validate before calling

def normalize_replacement(replacement: str | None) -> str:
    # deletion must be explicit in the tool call; make it explicit at your boundary
    return "" if replacement is None else replacement

tool.run(action="replace", paths=ps, pattern=pat, replacement=normalize_replacement(rep))

Try / catch

try:
    tool.run(action="replace", paths=ps, pattern=pat, replacement=rep)
except ValidationError as e:
    if "requires 'replacement'" in str(e):
        tool.run(action="replace", paths=ps, pattern=pat, replacement="")  # explicit delete
    else:
        raise

Prevention

When it happens

Trigger: Calling DaytonaFileTool with action='replace', paths and pattern set, but replacement omitted; attempting a find-only dry run (not supported by this schema).

Common situations: Agent tool call drops the last argument; caller wants to delete matched text and assumes omission means empty string — it must be explicit ''; misunderstanding that replace always performs the substitution.

Related errors


AI-assisted analysis of crewAIInc/crewAI@754d7323be (2026-08-15). Data as JSON: /api/errors/a12274c5254bd4af. Report an issue: GitHub.