{"record":{"id":"16ed458a3998cd77","repo":"langchain-ai/deepagents","slug":"an-offload-status-r-result-must-carry-a-reason","errorCode":null,"errorMessage":"An offload {status!r} result must carry a reason.","messagePattern":"An offload (.+?) result must carry a reason\\.","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"libs/code/deepagents_code/offload_middleware.py","lineNumber":674,"sourceCode":"        status: A non-compacting outcome.\n        messages: Messages left in the conversation.\n        tokens: Context estimate, unchanged by definition.\n        error: Reason, required for `denied` and `failed`.\n\n    Returns:\n        Typed result containing unchanged context statistics.\n\n    Raises:\n        ValueError: If a refusal carries no reason.\n    \"\"\"\n    if status in {\"denied\", \"failed\"} and not error:\n        # `error` is `str | None` on every status because the wire shape is one\n        # flat object, so the checker cannot make \"a refusal has a reason\" a\n        # compile-time fact. Enforce it at the single construction point\n        # instead: a reasonless refusal renders as the client's generic \"the\n        # server rejected the operation\", which tells the user nothing.\n        msg = f\"An offload {status!r} result must carry a reason.\"\n        raise ValueError(msg)\n    return {\n        \"status\": status,\n        \"messages_offloaded\": 0,\n        \"messages_kept\": messages,\n        \"tokens_before\": tokens,\n        \"tokens_after\": tokens,\n        \"archive_path\": None,\n        \"archive_ephemeral\": False,\n        \"error\": error,\n    }\n\n\nclass OffloadCompleteResponse(TypedDict):\n    \"\"\"Wire response for an attempt that finished without needing the client.\"\"\"\n\n    status: Literal[\"complete\"]\n    result: OffloadResult\n","sourceCodeStart":656,"sourceCodeEnd":692,"githubUrl":"https://github.com/langchain-ai/deepagents/blob/a1af029e6e73cb17c36bff823d227747b28e91e1/libs/code/deepagents_code/offload_middleware.py#L656-L692","documentation":"`unchanged_offload_result` builds the wire result for non-compacting offload outcomes. Because the flat wire shape types `error` as `str | None` for every status, a `denied` or `failed` result without a reason cannot be caught by the type checker, so this constructor enforces it at runtime: refusing without a reason would render as the client's generic 'server rejected the operation' message. Raises ValueError when status is denied/failed and error is falsy.","triggerScenarios":"Calling `unchanged_offload_result(\"denied\", messages=..., tokens=...)` or with \"failed\" without passing `error=`; HTTP-boundary code building a refusal result without a reason string.","commonSituations":"Custom server middleware denying offloads (rate limits, auth) without setting a reason; tests or handlers constructing refusal results directly.","solutions":["Pass a user-meaningful `error` string when building denied/failed results.","Use a status that permits no error (e.g. a complete/no-op status) if nothing was refused.","Route all refusal construction through `unchanged_offload_result` so the check stays in one place."],"exampleFix":"// before\nresult = unchanged_offload_result(\"denied\", messages=3, tokens=900)\n// after\nresult = unchanged_offload_result(\"denied\", messages=3, tokens=900, error=\"offload disabled for this thread\")","handlingStrategy":"validation","validationCode":"assert status not in {\"denied\", \"failed\"} or error, f\"{status!r} requires an error reason\"","typeGuard":"def refusal_has_reason(status: OffloadStatus, error: str | None) -> bool:\n    return status not in (\"denied\", \"failed\") or bool(error)","tryCatchPattern":"try:\n    result = unchanged_offload_result(status, messages=m, tokens=t, error=reason)\nexcept ValueError:\n    result = unchanged_offload_result(status, messages=m, tokens=t, error=DEFAULT_DENIAL_REASON)","preventionTips":["Always construct refusal results through unchanged_offload_result, never as raw dicts.","Give every denial path a concrete, user-readable reason string.","Add a table-driven test covering each status with/without error."],"tags":["validation","api-contract","offload"],"backgroundTag":"missing-required-field","analyzedSha":"a1af029e6e73cb17c36bff823d227747b28e91e1","analyzedAt":"2026-08-29T11:43:24.718Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}