{"record":{"id":"f9a2fec1c2e53c11","repo":"abhigyanpatwari/GitNexus","slug":"event-stream-artifact-path-must-be-transcripts-fi","errorCode":null,"errorMessage":"event-stream artifact path must be transcripts/<file>: {relative_path!r}","messagePattern":"event-stream artifact path must be transcripts/<file>: (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"eval/workflow_bench/runner_sessions.py","lineNumber":152,"sourceCode":"        tools.extend(GITNEXUS_MUTATING_TOOLS)\n    return tools\n\n\ndef _persist_parent_event_stream(\n    raw: bytes,\n    *,\n    output_dir: Path,\n    relative_path: str,\n    secrets: tuple[str, ...],\n) -> dict[str, Any]:\n    \"\"\"Persist only the complete event stream captured by the trusted parent.\"\"\"\n\n    # Parsing before persistence proves the artifact is complete structured\n    # evidence, rather than arbitrary output injected through a tool result.\n    events = _parse_parent_event_stream(raw)\n    relative = PurePosixPath(relative_path)\n    if relative.is_absolute() or len(relative.parts) != 2 or relative.parts[0] != \"transcripts\":\n        raise ValueError(f\"event-stream artifact path must be transcripts/<file>: {relative_path!r}\")\n    if any(part in {\"\", \".\", \"..\"} for part in relative.parts):\n        raise ValueError(f\"unsafe event-stream artifact path: {relative_path!r}\")\n\n    root = output_dir.expanduser().absolute()\n    root_mode = root.lstat().st_mode\n    if stat.S_ISLNK(root_mode) or not stat.S_ISDIR(root_mode) or root.resolve(strict=True) != root:\n        raise ValueError(f\"event-stream output root must be a real non-symlink directory: {root}\")\n    transcript_dir = root / relative.parts[0]\n    try:\n        transcript_dir.mkdir(mode=0o700)\n    except FileExistsError:\n        mode = transcript_dir.lstat().st_mode\n        if stat.S_ISLNK(mode) or not stat.S_ISDIR(mode):\n            raise ValueError(f\"event-stream artifact parent must be a real directory: {transcript_dir}\")\n    transcript_dir.chmod(0o700)\n\n    def redact_value(value: Any) -> Any:\n        if isinstance(value, str):","sourceCodeStart":134,"sourceCodeEnd":170,"githubUrl":"https://github.com/abhigyanpatwari/GitNexus/blob/d540b00184d71a896261ee02670da9a92d59d8f7/eval/workflow_bench/runner_sessions.py#L134-L170","documentation":"Thrown by persist_parent_event_stream (runner_sessions.py) when validating where an event-stream artifact may be written. The relative path must be exactly two POSIX parts — 'transcripts' as the first component and a single filename as the second — and must not be absolute. This narrow shape blocks arbitrary writes anywhere under the output root and is the first of two path checks.","triggerScenarios":"relative.is_absolute() is True, or len(relative.parts) != 2, or relative.parts[0] != 'transcripts'. E.g. the caller passed 'foo/bar.json', 'transcripts/a/b.json', an absolute '/x/y', or just 'file.json'.","commonSituations":"A session writer passed a nested path (transcripts/sub/file); a path from another component that does not prefix with 'transcripts'; an absolute path leaked in from a config; a caller that builds the path with the wrong base.","solutions":["Always pass a relative_path of the form 'transcripts/<single-filename>'.","If you need subdirectories, flatten to a single filename under transcripts/ (the harness layout does not nest further).","Strip any leading '/' and any 'transcripts/' prefix you may have doubled.","Centralize path construction in one helper that always emits the two-part form."],"exampleFix":"// before\npersist_parent_event_stream(raw, output_dir=out, relative_path='session_x/transcript.json', secrets=secrets)\n\n// after\npersist_parent_event_stream(raw, output_dir=out, relative_path='transcripts/session_x.json', secrets=secrets)","handlingStrategy":"validation","validationCode":"from pathlib import PurePosixPath\n\ndef is_valid_transcript_path(relative_path: str) -> bool:\n    p = PurePosixPath(relative_path)\n    return (not p.is_absolute() and len(p.parts) == 2 and p.parts[0] == 'transcripts')","typeGuard":"from pathlib import PurePosixPath\n\ndef is_valid_transcript_path(relative_path: str) -> bool:\n    p = PurePosixPath(relative_path)\n    return (not p.is_absolute() and len(p.parts) == 2 and p.parts[0] == 'transcripts')","tryCatchPattern":"try:\n    persist_parent_event_stream(raw, output_dir=out, relative_path=rp, secrets=secrets)\nexcept ValueError as e:\n    if 'must be transcripts/<file>' in str(e):\n        # rebuild relative_path as 'transcripts/<filename>' and retry\n        raise\n    raise","preventionTips":["Always build relative_path as f'transcripts/{filename}'.","Never nest subdirectories under transcripts/.","Centralize path construction in one helper that emits the two-part form.","Strip leading '/' and any duplicated 'transcripts/' prefix."],"tags":["security","path-traversal","validation","event-stream","workflow-bench"],"backgroundTag":null,"analyzedSha":"d540b00184d71a896261ee02670da9a92d59d8f7","analyzedAt":"2026-08-12T19:50:25.132Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}