langchain-ai/deepagents · error · ValueError
a failed write cannot have changed the file
Error message
a failed write cannot have changed the file
What it means
WriteResult's `__post_init__` enforces that a failed write (ok=False) cannot report changed=True. If the file changed, the write partially succeeded, and claiming failure-with-change would corrupt callers' assumptions about whether the on-disk config matches the intended mutation.
Source
Thrown at libs/code/deepagents_code/configuration/writer.py:47
error: str | None = None
def __post_init__(self) -> None:
"""Reject outcomes that cannot describe a real transaction.
Callers branch on `ok` alone, so a failure with no detail would surface
as a bare "could not be saved" with nothing to act on, and a change
recorded against a failed write would report an edit that never
reached the file.
Raises:
ValueError: If the three fields do not describe one outcome.
"""
if not self.ok and self.error is None:
msg = "a failed write must carry an error detail"
raise ValueError(msg)
if self.changed and not self.ok:
msg = "a failed write cannot have changed the file"
raise ValueError(msg)
if self.ok and self.error is not None:
msg = "a successful write cannot carry an error detail"
raise ValueError(msg)
def update_user_config(
mutate: Callable[[dict[str, Any]], bool],
*,
config_path: Path | None = None,
) -> WriteResult:
"""Serialize a read-modify-write of the user config and replace it atomically.
Writes the user tier only. The managed path is refused rather than trusted
to be unreachable.
A committed write to the default path also refreshes the shared process
resolver, so later reads see the new value. That refresh is best-effort and
never turns a landed write into a reported failure; seeView on GitHub (pinned to a1af029e6e)
Solutions
- If the file was actually updated, set ok=True and keep changed=True (optionally with no error)
- If the write truly failed, set changed=False and describe the failure in error
- For partial application, roll back or report the change via a distinct mechanism instead of contradictory flags
Example fix
// before WriteResult(ok=False, error="rename failed", changed=True) // after WriteResult(ok=False, error="rename failed", changed=False)
Defensive patterns
Strategy: validation
Validate before calling
def check_write_flags(ok: bool, changed: bool) -> str | None:
if changed and not ok:
return "failed write cannot report changed=True"
return None
issue = check_write_flags(ok, changed) # check before constructing Type guard
def is_consistent_write_result(ok: bool, changed: bool) -> bool:
return not (changed and not ok) Try / catch
try:
result = WriteResult(ok=False, error=err, changed=changed)
except ValueError:
result = WriteResult(ok=False, error=err, changed=False) Prevention
- Reset changed=False whenever the write path fails after mutation
- Derive changed from the mutate callable's return only on success
- Handle partial application explicitly instead of contradictory flags
When it happens
Trigger: Constructing WriteResult(ok=False, changed=True, error=<msg>) — e.g. a mutate callable returned True (made changes) but the overall write path was later marked failed, or hand-building a result in tests/tooling with contradictory fields.
Common situations: Partial-failure handling where the mutation applied but serialization/atomic rename failed; results aggregated from multiple attempts with mixed flags; test fixtures written by copy-paste.
Related errors
- a failed write must carry an error detail
- a successful write cannot carry an error detail
- a {self.status.health.value} snapshot must carry no data; an
- {what} must be absolute: {path}
- Could not determine the home directory: set $HOME, or set DE
AI-assisted analysis of langchain-ai/deepagents@a1af029e6e (2026-08-29).
Data as JSON: /api/errors/a185e44429b3b785.
Report an issue: GitHub.