langchain-ai/deepagents · error · ValueError
a successful write cannot carry an error detail
Error message
a successful write cannot carry an error detail
What it means
WriteResult's `__post_init__` enforces that a successful write (ok=True) must not carry an `error` detail. An error attached to a success would mislead callers and UIs that surface errors to users, so the combination is rejected at construction.
Source
Thrown at libs/code/deepagents_code/configuration/writer.py:50
"""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; see
`refresh_shared_resolver`.
Args:View on GitHub (pinned to a1af029e6e)
Solutions
- Set error=None when ok=True (the default)
- On successful retry, construct a fresh WriteResult instead of mutating/reusing the failed one
- Surface non-fatal warnings through a separate channel, not the error field
Example fix
// before WriteResult(ok=True, error="previous attempt timed out", changed=True) // after WriteResult(ok=True, error=None, changed=True)
Defensive patterns
Strategy: validation
Validate before calling
def check_write_flags(ok: bool, error: str | None) -> str | None:
if ok and error is not None:
return "successful write must not carry an error detail"
return None
issue = check_write_flags(ok, err) # check before constructing Type guard
def is_clean_success(ok: bool, error: str | None) -> bool:
return not ok or error is None Try / catch
try:
result = WriteResult(ok=True, error=stale_error, changed=True)
except ValueError:
result = WriteResult(ok=True, error=None, changed=True) Prevention
- Construct a fresh WriteResult on retry success; never mutate the failed one
- Default error to None and only set it on the failure branch
- Report warnings via a dedicated field or logging, not error
When it happens
Trigger: Constructing WriteResult(ok=True, error=<msg>, ...) — e.g. code that always sets error from a previous attempt's exception without clearing it on retry success, or hand-built results in tests.
Common situations: Retry loops that keep the first attempt's exception object while succeeding on the second; copying a failed result and flipping ok to True; tests with fixture objects that include a leftover error message.
Related errors
- a failed write must carry an error detail
- a failed write cannot have changed the file
- 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/b2d5f6f539964f71.
Report an issue: GitHub.