{"record":{"id":"477aa79e40162937","repo":"bytedance/deer-flow","slug":"binary-content-cannot-be-saved-as-an-artifact","errorCode":null,"errorMessage":"Binary content cannot be saved as an artifact","messagePattern":"Binary content cannot be saved as an artifact","errorType":"http","errorClass":"HTTPException","httpStatus":415,"severity":"error","filePath":"backend/app/gateway/routers/artifacts.py","lineNumber":110,"sourceCode":"    if b\"\\x00\" in current:\n        raise HTTPException(status_code=415, detail=\"Binary artifacts cannot be edited\")\n    try:\n        current.decode(\"utf-8\")\n    except UnicodeDecodeError:\n        raise HTTPException(status_code=415, detail=\"Only UTF-8 text artifacts can be edited\") from None\n\n    current_sha256 = hashlib.sha256(current).hexdigest()\n    if current_sha256 != expected_sha256:\n        raise HTTPException(status_code=412, detail=\"Artifact changed since it was opened\")\n    return current, file_stat\n\n\ndef _encode_artifact_update(content: str) -> bytes:\n    encoded = content.encode(\"utf-8\")\n    if len(encoded) > MAX_EDITABLE_ARTIFACT_BYTES:\n        raise HTTPException(status_code=413, detail=\"Artifact is too large to edit\")\n    if b\"\\x00\" in encoded:\n        raise HTTPException(status_code=415, detail=\"Binary content cannot be saved as an artifact\")\n    return encoded\n\n\ndef _replace_artifact_atomically(actual_path: Path, content: bytes, file_stat: os.stat_result) -> None:\n    temp_fd, temp_path_str = tempfile.mkstemp(prefix=_ARTIFACT_EDIT_TEMP_PREFIX, dir=actual_path.parent)\n    temp_path = Path(temp_path_str)\n    try:\n        # Preserve ownership where possible and keep replacement permissions\n        # scoped to the owner/group. The shared outputs directory allows a\n        # mounted sandbox to reach the file without making it world-writable.\n        if hasattr(os, \"fchown\"):\n            try:\n                os.fchown(temp_fd, file_stat.st_uid, file_stat.st_gid)\n            except OSError:\n                logger.debug(\"Could not preserve artifact ownership: %s\", actual_path, exc_info=True)\n        # Windows has no fchmod and uses ACLs rather than POSIX mode bits.\n        # Keep the mkstemp permissions there; retain the existing POSIX\n        # behavior on platforms that expose descriptor-based chmod.","sourceCodeStart":92,"sourceCodeEnd":128,"githubUrl":"https://github.com/bytedance/deer-flow/blob/1dd6ba1acb03700589994b0366c5d1c7d05e2eff/backend/app/gateway/routers/artifacts.py#L92-L128","documentation":"HTTP 415 from _encode_artifact_update: the incoming content string encodes to UTF-8 bytes containing a NUL byte. The save path mirrors the load-side binary check: the artifacts editor must remain text-only end to end, so binary payloads (even those smuggled inside a JSON string) are rejected before any disk write.","triggerScenarios":"PUT edit-artifact with a content string containing '\\u0000' — programmatic clients injecting serialized/binary data through the JSON content field, or corrupted editor buffers.","commonSituations":"Scripts treating the edit endpoint as a generic file-write API, copy-paste of terminal control-laden output, or upstream bugs producing NULs in strings.","solutions":["Strip or reject NUL characters in content before sending (legitimate text should never contain them)","Use the sandbox write_file tool for anything binary — the artifacts editor is text-only by contract","Investigate why the content contains NULs; usually a corrupted source or a misused API"],"exampleFix":"// before\nawait client.put(EDIT_URL, json={\"path\": p, \"content\": text_with_nuls, \"sha256\": sha})\n\n// after\nassert !text.includes(\"\\u0000\");  // guard client-side\nawait client.put(EDIT_URL, json={\"path\": p, \"content\": text, \"sha256\": sha})","handlingStrategy":"validation","validationCode":"def content_is_nul_free(content: str) -> bool:\n    return \"\\x00\" not in content","typeGuard":"def is_savable_content(content: str) -> bool:\n    encoded = content.encode(\"utf-8\")\n    return (len(encoded) <= 2 * 1024 * 1024\n            and b\"\\x00\" not in encoded)","tryCatchPattern":null,"preventionTips":["Validate editor buffers client-side for NUL before enabling Save","Do not use the artifact edit API as a binary file writer — route binary through sandbox tools","Sanitize pasted content in the UI (strip control characters except \\n\\t\\r)"],"tags":["api","artifacts","binary","http-415","input-validation"],"backgroundTag":null,"analyzedSha":"1dd6ba1acb03700589994b0366c5d1c7d05e2eff","analyzedAt":"2026-08-14T21:20:34.804Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}