{"record":{"id":"1e9249f05b516f1e","repo":"crewAIInc/crewAI","slug":"action-replace-requires-paths-list-of-file-pa","errorCode":null,"errorMessage":"action='replace' requires 'paths' (list of file paths).","messagePattern":"action='replace' requires 'paths' \\(list of file paths\\)\\.","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"lib/crewai-tools/src/crewai_tools/tools/daytona_sandbox_tool/daytona_file_tool.py","lineNumber":208,"sourceCode":"            raise ValueError(\n                \"action='append' requires 'content'. Pass the chunk to append \"\n                \"in the 'content' field.\"\n            )\n        if self.action == \"move\" and not self.destination:\n            raise ValueError(\"action='move' requires 'destination'.\")\n        if self.action == \"find\" and not self.pattern:\n            raise ValueError(\n                \"action='find' requires 'pattern' (text to search for inside files).\"\n            )\n        if self.action == \"search\" and not self.pattern:\n            raise ValueError(\"action='search' requires 'pattern' (glob, e.g. '*.py').\")\n        if self.action == \"chmod\" and not (self.mode or self.owner or self.group):\n            raise ValueError(\n                \"action='chmod' requires at least one of 'mode', 'owner', or 'group'.\"\n            )\n        if self.action == \"replace\":\n            if not self.paths:\n                raise ValueError(\n                    \"action='replace' requires 'paths' (list of file paths).\"\n                )\n            if not self.pattern:\n                raise ValueError(\"action='replace' requires 'pattern'.\")\n            if self.replacement is None:\n                raise ValueError(\"action='replace' requires 'replacement'.\")\n        return self\n\n\nclass DaytonaFileTool(DaytonaBaseTool):\n    \"\"\"Read, write, and manage files inside a Daytona sandbox.\n\n    Notes:\n      - Most useful with `persistent=True` or an explicit `sandbox_id`. With the\n        default ephemeral mode, files disappear when this tool call finishes.\n    \"\"\"\n\n    name: str = \"Daytona Sandbox Files\"","sourceCodeStart":190,"sourceCodeEnd":226,"githubUrl":"https://github.com/crewAIInc/crewAI/blob/754d7323beb2fd042e33444a115ea2d5a47193f0/lib/crewai-tools/src/crewai_tools/tools/daytona_sandbox_tool/daytona_file_tool.py#L190-L226","documentation":"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.","triggerScenarios":"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'.","commonSituations":"Caller mixes up path (single file) and paths (list) for the replace action; agent emits paths=None; upstream glob returned an empty list.","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."],"exampleFix":"# before\ntool.run(action=\"replace\", path=\"/workspace/a.py\", pattern=\"foo\", replacement=\"bar\")  # ValueError: paths missing\n\n# after\ntool.run(action=\"replace\", paths=[\"/workspace/a.py\"], pattern=\"foo\", replacement=\"bar\")","handlingStrategy":"validation","validationCode":"def validate_replace(paths: list[str] | None, pattern: str | None, replacement: str | None) -> None:\n    if not paths:\n        raise ValueError(\"replace requires non-empty 'paths' list\")\n    if not pattern:\n        raise ValueError(\"replace requires 'pattern'\")\n    if replacement is None:\n        raise ValueError(\"replace requires explicit 'replacement' (may be '')\")\n\nvalidate_replace(paths, pattern, replacement)","typeGuard":null,"tryCatchPattern":"try:\n    tool.run(action=\"replace\", paths=ps, pattern=pat, replacement=rep)\nexcept ValidationError as e:\n    if \"requires 'paths'\" in str(e):\n        ps = [p] if isinstance(p, str) else list(ps or [])\n        tool.run(action=\"replace\", paths=ps, pattern=pat, replacement=rep)\n    else:\n        raise","preventionTips":["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."],"tags":["daytona","pydantic","validation","input-validation","filesystem"],"backgroundTag":null,"analyzedSha":"754d7323beb2fd042e33444a115ea2d5a47193f0","analyzedAt":"2026-08-15T04:06:56.746Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}