abhigyanpatwari/GitNexus · error · SandboxError

transcript artifact metadata is malformed

Error message

transcript artifact metadata is malformed

What it means

After source is validated, the path must be a str and the sha256 must match the regex [0-9a-f]{64} (lowercase hex, exactly 64 chars). A non-string path, an uppercase-hex digest, a sha1 (40 chars), or a non-hex digest is treated as malformed metadata and rejected before any file is opened.

Source

Thrown at eval/workflow_bench/evolve.py:336

        if stat.S_ISLNK(metadata.st_mode) or not stat.S_ISDIR(metadata.st_mode):
            raise SandboxError(f"results artifact parent must be a real directory: {current}")
        if transcript and stat.S_IMODE(metadata.st_mode) & 0o077:
            raise SandboxError(f"transcript artifact parent must be owner-only: {current}")
    return root / Path(*relative.parts)


def _transcript_artifact_metadata(metadata: Any) -> tuple[str, str, int]:
    """Validate transcript metadata without touching any host path."""

    if not isinstance(metadata, dict) or set(metadata) != {"path", "sha256", "bytes", "source"}:
        raise SandboxError("transcript artifact metadata must contain only path, sha256, bytes, and source")
    relative = metadata["path"]
    expected_digest = metadata["sha256"]
    expected_size = metadata["bytes"]
    if metadata["source"] != runner_sessions.PARENT_EVENT_STREAM_SOURCE:
        raise SandboxError("transcript artifact source is not the parent event stream")
    if not isinstance(relative, str) or not re.fullmatch(r"[0-9a-f]{64}", str(expected_digest)):
        raise SandboxError("transcript artifact metadata is malformed")
    if not isinstance(expected_size, int) or isinstance(expected_size, bool):
        raise SandboxError("transcript artifact byte count must be an integer")
    if expected_size < 0 or expected_size > runner.MAX_TRANSCRIPT_BYTES:
        raise SandboxError("transcript artifact exceeds the bounded run-output limit")
    return relative, expected_digest, expected_size


def _normalized_transcript_artifact_path(relative_value: str) -> str:
    """Apply the transcript path contract without touching the filesystem."""

    relative = PurePosixPath(relative_value)
    if (
        relative.is_absolute()
        or len(relative.parts) != 2
        or relative.parts[0] != "transcripts"
        or any(part in {"", ".", ".."} for part in relative.parts)
    ):
        raise SandboxError(f"unsafe results artifact path: {relative_value!r}")

View on GitHub (pinned to d540b00184)

Solutions

  1. Normalize the digest to lowercase 64-char hex: `hashlib.sha256(data).hexdigest()` already yields this.
  2. Ensure path is a plain str (call str() if it is a Path), matching the 'transcripts/<name>' contract.
  3. Regenerate the row with the current runner so digest and path are written canonically.

Example fix

# before
{"path": "transcripts/r.json", "sha256": "ABCDEF0123...", "bytes": 12, "source": "parent-captured-stream-json"}

# after
import hashlib
digest = hashlib.sha256(blob).hexdigest()  # lowercase, 64 hex
row = {"path": "transcripts/r.json", "sha256": digest, "bytes": 12, "source": PARENT_EVENT_STREAM_SOURCE}
Defensive patterns

Strategy: validation

Validate before calling

import re
from pathlib import PurePosixPath

HEX64 = re.compile(r"[0-9a-f]{64}")

def transcript_path_and_digest_ok(metadata: dict) -> bool:
    rel = metadata.get("path")
    digest = metadata.get("sha256")
    return isinstance(rel, str) and bool(HEX64.fullmatch(str(digest)))

Type guard

import re
HEX64 = re.compile(r"[0-9a-f]{64}")

def is_valid_sha256_hex(value: object) -> bool:
    return isinstance(value, str) and bool(HEX64.fullmatch(value))

Prevention

When it happens

Trigger: path is None/a list/a Path object; sha256 is uppercase ('ABCDEF...'), a sha1 (40 chars), a sha256 with dashes, or contains non-hex characters; a base64 digest was recorded by mistake.

Common situations: A runner that uppercased the digest; sha1 from a legacy path; a path stored as a structured value; truncation/copy errors shortening the digest.

Understand the failure class

Related errors


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