bytedance/deer-flow · error · HTTPException
Artifact changed since it was opened
Error message
Artifact changed since it was opened
What it means
HTTP 412 from _load_editable_artifact: sha256 of the current file bytes does not match the expected_sha256 the client sent with the edit. This is optimistic-concurrency control: the artifact changed on disk since the client opened it, and saving would silently clobber someone else's (or another run's) writes.
Source
Thrown at backend/app/gateway/routers/artifacts.py:101
raise HTTPException(status_code=415, detail="Symlinked artifacts cannot be edited")
if not stat.S_ISREG(file_stat.st_mode):
raise HTTPException(status_code=400, detail=f"Path is not a file: {path}")
if file_stat.st_size > MAX_EDITABLE_ARTIFACT_BYTES:
raise HTTPException(status_code=413, detail="Artifact is too large to edit")
current = actual_path.read_bytes()
if len(current) > MAX_EDITABLE_ARTIFACT_BYTES:
raise HTTPException(status_code=413, detail="Artifact is too large to edit")
if b"\x00" in current:
raise HTTPException(status_code=415, detail="Binary artifacts cannot be edited")
try:
current.decode("utf-8")
except UnicodeDecodeError:
raise HTTPException(status_code=415, detail="Only UTF-8 text artifacts can be edited") from None
current_sha256 = hashlib.sha256(current).hexdigest()
if current_sha256 != expected_sha256:
raise HTTPException(status_code=412, detail="Artifact changed since it was opened")
return current, file_stat
def _encode_artifact_update(content: str) -> bytes:
encoded = content.encode("utf-8")
if len(encoded) > MAX_EDITABLE_ARTIFACT_BYTES:
raise HTTPException(status_code=413, detail="Artifact is too large to edit")
if b"\x00" in encoded:
raise HTTPException(status_code=415, detail="Binary content cannot be saved as an artifact")
return encoded
def _replace_artifact_atomically(actual_path: Path, content: bytes, file_stat: os.stat_result) -> None:
temp_fd, temp_path_str = tempfile.mkstemp(prefix=_ARTIFACT_EDIT_TEMP_PREFIX, dir=actual_path.parent)
temp_path = Path(temp_path_str)
try:
# Preserve ownership where possible and keep replacement permissions
# scoped to the owner/group. The shared outputs directory allows aView on GitHub (pinned to 1dd6ba1acb)
Solutions
- Re-open the artifact to get fresh content and the new sha256, merge your changes, then save again
- Avoid concurrent editors on the same thread's artifacts — coordinate through one session
- Clients should always send the sha256 obtained from the most recent GET, not a cached one
Example fix
// before: sha256 cached from an old listing → 412
payload = {"path": p, "content": new_text, "sha256": cached_sha}
// after: fetch fresh content+sha right before saving
fresh = await client.get(artifact_url(p))
payload = {"path": p, "content": merge(fresh.text, local_edits), "sha256": sha256(fresh_bytes)}
await client.put(EDIT_URL, json=payload) Defensive patterns
Strategy: validation
Validate before calling
import hashlib
def sha_matches(actual_path: str, expected_sha256: str) -> bool:
h = hashlib.sha256()
with open(actual_path, "rb") as f:
for chunk in iter(lambda: f.read(1 << 16), b""):
h.update(chunk)
return h.hexdigest() == expected_sha256 Try / catch
for attempt in range(3):
resp = await client.put(EDIT_URL, json=payload)
if resp.status_code != 412:
break
fresh = await client.get(artifact_url(path)) # reload
payload["content"] = merge(fresh.text, payload["content"])
payload["sha256"] = sha256_of(fresh) # then retry
else:
raise ConflictError("artifact kept changing") Prevention
- Always fetch fresh content+sha immediately before save
- On 412, reload-and-merge; never blind-retry the same payload
- Lock the UI editor while a run that writes the same artifact is active
When it happens
Trigger: PUT edit-artifact where two sessions/tabs edit the same file, the agent rewrites the artifact between open and save, or a run re-executed and regenerated outputs while an editor was open.
Common situations: Multi-tab editing, long-held editor sessions across run retries, mobile+desktop editing the same artifact, or automated clients caching stale sha256 from an old listing.
Related errors
- Only files in /mnt/user-data/outputs can be edited
- Skill archives cannot be edited in the artifacts panel
- Artifact not found: {path}
- Symlinked artifacts cannot be edited
- Path is not a file: {path}
AI-assisted analysis of bytedance/deer-flow@1dd6ba1acb (2026-08-14).
Data as JSON: /api/errors/813ce8e9e8bd5b1e.
Report an issue: GitHub.