langchain-ai/deepagents · error · OSError

temporary artifact identity changed

Error message

temporary artifact identity changed

What it means

Raised by `_delete_temp_artifact_file` when an `lstat` of the artifact's path does not match the identity recorded at creation: the file is no longer a regular file, or its `st_dev`/`st_ino` differ from the `file_device`/`file_inode` stored in the `AutoTempArtifact`. The library throws this to prevent a symlink-swap or replacement race from causing it to unlink the wrong file.

Source

Thrown at libs/code/deepagents_code/auto_mode.py:1124

                )
            ]
        }
    )


def _delete_temp_artifact_file(artifact: AutoTempArtifact) -> None:
    file_path = Path(artifact["file_path"])
    if not file_path.name.startswith(_TEMP_ARTIFACT_PREFIX):
        msg = "temporary artifact provenance is invalid"
        raise OSError(msg)
    file_stat = file_path.lstat()
    if (
        not stat.S_ISREG(file_stat.st_mode)
        or file_stat.st_dev != artifact["file_device"]
        or file_stat.st_ino != artifact["file_inode"]
    ):
        msg = "temporary artifact identity changed"
        raise OSError(msg)
    file_path.unlink()


def _summarize_value(key: str, value: object, *, depth: int = 0) -> object:
    if depth >= _MAX_ARGUMENT_DEPTH:
        return "[nested value omitted]"
    if _SECRET_KEY_RE.search(key):
        return "[redacted credential value]"
    if key.lower() in {"content", "new_string", "old_string", "new_str"} and isinstance(
        value, str
    ):
        return {"character_count": len(value), "content_omitted": True}
    if isinstance(value, str):
        return value[:4000]
    if isinstance(value, Mapping):
        return {
            str(child_key): _summarize_value(
                str(child_key), child_value, depth=depth + 1

View on GitHub (pinned to a1af029e6e)

Solutions

  1. Retry the whole flow: re-create the artifact with `create_temp_artifact` rather than reusing the stale record
  2. Check for temp-file cleaners (`systemd-tmpfiles`, `tmpwatch`, cron scripts) racing the agent and exclude the prefix or shorten the artifact lifetime
  3. If the file is legitimately already gone, treat the deletion as done and drop the stale artifact record from state
  4. Never delete the same artifact record twice — track the returned allocation lifecycle
Defensive patterns

Strategy: try-catch

Validate before calling

import os, stat
from pathlib import Path

def artifact_identity_intact(artifact) -> bool:
    try:
        st = Path(artifact["file_path"]).lstat()
    except FileNotFoundError:
        return False
    return stat.S_ISREG(st.st_mode) and st.st_dev == artifact["file_device"] and st.st_ino == artifact["file_inode"]

Try / catch

try:
    delete_temp_artifact(artifact)
except OSError as exc:
    if "identity changed" in str(exc):
        discard_stale_record()  # file already gone or replaced; nothing safe to unlink
    else:
        raise

Prevention

When it happens

Trigger: Calling `delete_temp_artifact` after the temp file was deleted and recreated, replaced by a symlink or different inode, or moved/renamed — any change of device or inode between `create_temp_artifact` and the delete call.

Common situations: Another process or a cleanup daemon (`tmpwatch`, `systemd-tmpfiles`) removing and re-creating files in the temp dir; tests reusing artifact records across temp dirs; the same artifact record being deleted twice, where the second delete fails because the original inode is gone; an attacker swapping the file (the check exists for this).

Related errors


AI-assisted analysis of langchain-ai/deepagents@a1af029e6e (2026-08-29). Data as JSON: /api/errors/0447813fa1f2e266. Report an issue: GitHub.