FoundationAgents/OpenManus · error · RuntimeError

Source path is a directory but destination is a file: {src_p

Error message

Source path is a directory but destination is a file: {src_path}

What it means

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.

Source

Thrown at app/sandbox/core/sandbox.py:298

                # Write stream to temporary file
                tar_path = os.path.join(tmp_dir, "temp.tar")
                with open(tar_path, "wb") as f:
                    for chunk in stream:
                        f.write(chunk)

                # Extract file
                with tarfile.open(tar_path) as tar:
                    members = tar.getmembers()
                    if not members:
                        raise FileNotFoundError(f"Source file is empty: {src_path}")

                    # If destination is a directory, we should preserve relative path structure
                    if os.path.isdir(dst_path):
                        tar.extractall(dst_path)
                    else:
                        # If destination is a file, we only extract the source file's content
                        if len(members) > 1:
                            raise RuntimeError(
                                f"Source path is a directory but destination is a file: {src_path}"
                            )

                        with open(dst_path, "wb") as dst:
                            src_file = tar.extractfile(members[0])
                            if src_file is None:
                                raise RuntimeError(
                                    f"Failed to extract file: {src_path}"
                                )
                            dst.write(src_file.read())

        except docker.errors.NotFound:
            raise FileNotFoundError(f"Source file not found: {src_path}")
        except Exception as e:
            raise RuntimeError(f"Failed to copy file: {e}")

    async def copy_to(self, src_path: str, dst_path: str) -> None:
        """Copies a file to the container.

View on GitHub (pinned to 52a13f2a57)

Solutions

  1. Copy directories to a directory destination: ensure dst_path does not exist or is a directory (os.makedirs(dst, exist_ok=True)).
  2. Delete or rename the stale file occupying the destination before the directory copy.
  3. Decide per-source: use run_command('test -d') to branch file vs directory handling.

Example fix

# before
open(out_txt, "w").close()               # dst exists as a file
await sandbox.copy_from("out/", out_txt)   # RuntimeError

# after
if os.path.isfile(out_path):
    os.remove(out_path)
os.makedirs(out_path, exist_ok=True)
await sandbox.copy_from("out/", out_path)
Defensive patterns

Strategy: validation

Validate before calling

is_dir_src = "d" in (await sandbox.run_command(f"ls -ld {shlex.quote(src)}")).split()[0]
if is_dir_src and os.path.exists(dst) and not os.path.isdir(dst):
    os.remove(dst)
if is_dir_src:
    os.makedirs(dst, exist_ok=True)

Try / catch

try:
    await sandbox.copy_from(src, dst)
except RuntimeError as e:
    if "directory but destination is a file" in str(e):
        os.remove(dst); os.makedirs(dst)
        await sandbox.copy_from(src, dst)
    else:
        raise

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of FoundationAgents/OpenManus@52a13f2a57 (2026-08-15). Data as JSON: /api/errors/f8aa4b4b6dd639d3. Report an issue: GitHub.