{"record":{"id":"f8aa4b4b6dd639d3","repo":"FoundationAgents/OpenManus","slug":"source-path-is-a-directory-but-destination-is-a-fi","errorCode":null,"errorMessage":"Source path is a directory but destination is a file: {src_path}","messagePattern":"Source path is a directory but destination is a file: (.+?)","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"app/sandbox/core/sandbox.py","lineNumber":298,"sourceCode":"                # Write stream to temporary file\n                tar_path = os.path.join(tmp_dir, \"temp.tar\")\n                with open(tar_path, \"wb\") as f:\n                    for chunk in stream:\n                        f.write(chunk)\n\n                # Extract file\n                with tarfile.open(tar_path) as tar:\n                    members = tar.getmembers()\n                    if not members:\n                        raise FileNotFoundError(f\"Source file is empty: {src_path}\")\n\n                    # If destination is a directory, we should preserve relative path structure\n                    if os.path.isdir(dst_path):\n                        tar.extractall(dst_path)\n                    else:\n                        # If destination is a file, we only extract the source file's content\n                        if len(members) > 1:\n                            raise RuntimeError(\n                                f\"Source path is a directory but destination is a file: {src_path}\"\n                            )\n\n                        with open(dst_path, \"wb\") as dst:\n                            src_file = tar.extractfile(members[0])\n                            if src_file is None:\n                                raise RuntimeError(\n                                    f\"Failed to extract file: {src_path}\"\n                                )\n                            dst.write(src_file.read())\n\n        except docker.errors.NotFound:\n            raise FileNotFoundError(f\"Source file not found: {src_path}\")\n        except Exception as e:\n            raise RuntimeError(f\"Failed to copy file: {e}\")\n\n    async def copy_to(self, src_path: str, dst_path: str) -> None:\n        \"\"\"Copies a file to the container.","sourceCodeStart":280,"sourceCodeEnd":316,"githubUrl":"https://github.com/FoundationAgents/OpenManus/blob/52a13f2a57d8c7f6737eefb02ccf569594d44273/app/sandbox/core/sandbox.py#L280-L316","documentation":"Raised as RuntimeError by DockerSandbox.copy_from (app/sandbox/core/sandbox.py:298): the source path in the container is a directory (the tar contains more than one member) but the destination on the host is a plain file, so there is nowhere to put multiple entries. The code only takes the single-member branch when dst is not an existing directory.","triggerScenarios":"dst_path exists as a file (created by a previous single-file copy) while src_path is now a directory; caller passes a filename as dst intending to copy a directory; src file replaced by a same-named directory between calls.","commonSituations":"Reusing one host destination path for what sometimes is a file and sometimes a directory; agent pipelines that first copy out.txt then try to copy an out/ tree to the same name.","solutions":["Copy directories to a directory destination: ensure dst_path does not exist or is a directory (os.makedirs(dst, exist_ok=True)).","Delete or rename the stale file occupying the destination before the directory copy.","Decide per-source: use run_command('test -d') to branch file vs directory handling."],"exampleFix":"# before\nopen(out_txt, \"w\").close()               # dst exists as a file\nawait sandbox.copy_from(\"out/\", out_txt)   # RuntimeError\n\n# after\nif os.path.isfile(out_path):\n    os.remove(out_path)\nos.makedirs(out_path, exist_ok=True)\nawait sandbox.copy_from(\"out/\", out_path)","handlingStrategy":"validation","validationCode":"is_dir_src = \"d\" in (await sandbox.run_command(f\"ls -ld {shlex.quote(src)}\")).split()[0]\nif is_dir_src and os.path.exists(dst) and not os.path.isdir(dst):\n    os.remove(dst)\nif is_dir_src:\n    os.makedirs(dst, exist_ok=True)","typeGuard":null,"tryCatchPattern":"try:\n    await sandbox.copy_from(src, dst)\nexcept RuntimeError as e:\n    if \"directory but destination is a file\" in str(e):\n        os.remove(dst); os.makedirs(dst)\n        await sandbox.copy_from(src, dst)\n    else:\n        raise","preventionTips":["Match destination kind to source kind (dir->dir, file->file)","Never reuse one host path for both file and directory copies","test -d the source before choosing the destination"],"tags":["sandbox","copy","tar","filesystem","type-mismatch"],"backgroundTag":null,"analyzedSha":"52a13f2a57d8c7f6737eefb02ccf569594d44273","analyzedAt":"2026-08-15T02:33:49.993Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}