abhigyanpatwari/GitNexus · error · SandboxError

sandbox_copy path is not valid UTF-8: {relative!s}

Error message

sandbox_copy path is not valid UTF-8: {relative!s}

What it means

Raised by _validate_manifest_path when relative.as_posix().encode('utf-8') throws UnicodeEncodeError. sandbox_copy paths must round-trip cleanly through UTF-8 so the manifest JSON and digests stay deterministic; any path that cannot be UTF-8 encoded (e.g. containing surrogateescape codepoints from os.listdir on Linux) is rejected before it enters the manifest.

Source

Thrown at eval/workflow_bench/task_assets.py:892

        view = view[written:]


def _mutation_identity(metadata: os.stat_result) -> tuple[int, int, int, int, int, int]:
    return (
        metadata.st_dev,
        metadata.st_ino,
        metadata.st_mode,
        metadata.st_size,
        metadata.st_mtime_ns,
        metadata.st_ctime_ns,
    )


def _validate_manifest_path(relative: PurePosixPath) -> None:
    try:
        path_bytes = len(relative.as_posix().encode("utf-8"))
    except UnicodeEncodeError as exc:
        raise SandboxError(f"sandbox_copy path is not valid UTF-8: {relative!s}") from exc
    if path_bytes > MAX_TASK_ASSET_PATH_BYTES:
        raise SandboxError("sandbox_copy exceeds the path byte limit")


def _manifest_digest(entries: tuple[AssetManifestEntry, ...]) -> str:
    payload = [
        {
            "kind": entry.kind,
            "link_target": entry.link_target,
            "mode": entry.mode,
            "path": entry.path.as_posix(),
            "sha256": entry.sha256,
            "size": entry.size,
        }
        for entry in entries
    ]
    return hashlib.sha256(json.dumps(payload, sort_keys=True, separators=(",", ":")).encode()).hexdigest()

View on GitHub (pinned to d540b00184)

Solutions

  1. Normalize the sandbox_copy path to a clean str before building the declaration: decode bytes with the actual filesystem encoding and re-encode strict utf-8, replacing or rejecting non-decodable names.
  2. Rename the offending source file on disk to a valid UTF-8 name so the declaration path is clean.
  3. If you genuinely need non-UTF-8 paths, they are unsupported by this module — exclude them from sandbox_copy or pre-process them into UTF-8 equivalents.

Example fix

# before: raw bytes path leaks surrogates into the declaration
raw = os.fsdecode(os.listdir(b'/repo')[0])   # may contain surrogates
source = PurePosixPath(raw)

# after: enforce clean UTF-8 at the boundary
raw = os.listdir('/repo')[0]
source = PurePosixPath(raw)
assert source.as_posix().encode('utf-8')   # fails fast on bad input
Defensive patterns

Strategy: validation

Validate before calling

from pathlib import PurePosixPath

def assert_utf8_path(relative: PurePosixPath) -> None:
    posix = relative.as_posix()
    try:
        posix.encode('utf-8')
    except UnicodeEncodeError:
        raise ValueError(f'sandbox_copy path is not valid UTF-8: {posix!r}') from None

# Run on every declared source/target before building the task dict.

Type guard

from pathlib import PurePosixPath

def is_utf8_path(relative: PurePosixPath) -> bool:
    try:
        relative.as_posix().encode('utf-8')
        return True
    except UnicodeEncodeError:
        return False

Try / catch

from .proposer_sandbox import SandboxError

task_paths = [PurePosixPath(d['source']) for d in task.get('sandbox_copy', [])]
if not all(is_utf8_path(p) for p in task_paths):
    # sanitize / rename offending files before capture instead of catching post-hoc
    task['sandbox_copy'] = [d for d in task['sandbox_copy'] if is_utf8_path(PurePosixPath(d['source']))]

try:
    stage_task_assets(task, ...)
except SandboxError as exc:
    if 'not valid UTF-8' in str(exc):
        # rename the offending source file to clean UTF-8, then retry prepare
        ...
    raise

Prevention

When it happens

Trigger: A sandbox_copy declaration whose source/target path was constructed from raw os.scandir/os.listdir bytes decoded with surrogateescape, or assembled from a bytes path that included invalid UTF-8 sequences, then passed through PurePosixPath. The surrogate codepoints (U+DC80..U+DCFF) survive into PurePosixPath but fail .encode('utf-8').

Common situations: Mixing bytes-based path handling with str PurePosixPath; copying assets from a filesystem with legacy non-UTF-8 filenames (Latin-1,Shift-JIS); test fixtures that build paths from arbitrary bytes; cross-platform path joining that injects lone surrogates.

Related errors


AI-assisted analysis of abhigyanpatwari/GitNexus@d540b00184 (2026-08-12). Data as JSON: /api/errors/8cf957adc1258c30. Report an issue: GitHub.