langchain-ai/deepagents · error · RuntimeError

Manual mode could not be persisted

Error message

Manual mode could not be persisted

What it means

After invoking _on_switch_to_manual for a 'switch_manual' decision, a falsy return means the manual mode was not persisted (e.g. the Store write failed). The turn raises RuntimeError because continuing would leave approval state inconsistent with the user's explicit choice.

Source

Thrown at libs/code/deepagents_code/tui/textual_adapter.py:3669

                                        args = action_request.get("args", {})
                                        if isinstance(args, dict):
                                            file_op_tracker.mark_hitl_approved(
                                                tool_name, args
                                            )

                            elif decision_type == "switch_manual":
                                if adapter._on_switch_to_manual is None:
                                    msg = "Manual mode callback is unavailable"
                                    raise RuntimeError(msg)  # noqa: TRY301  # shared turn error rendering owns this failure
                                callback_result = adapter._on_switch_to_manual()
                                switched = (
                                    await callback_result
                                    if inspect.isawaitable(callback_result)
                                    else callback_result
                                )
                                if not switched:
                                    msg = "Manual mode could not be persisted"
                                    raise RuntimeError(msg)  # noqa: TRY301  # shared turn error rendering owns this failure
                                decisions = [
                                    cast("HITLDecision", {"type": "switch_manual"})
                                    for _ in action_requests
                                ]

                            elif decision_type == "approve":
                                decisions = [
                                    ApproveDecision(type="approve")
                                    for _ in action_requests
                                ]
                                tool_msgs = _interrupt_tool_rows(
                                    review_namespace,
                                    action_requests,
                                    adapter._current_tool_messages,
                                )
                                for tool_msg in tool_msgs:
                                    _set_running_unless_deferred(tool_msg)
                                    adapter._sync_tool_widget(tool_msg)

View on GitHub (pinned to a1af029e6e)

Solutions

  1. Check what _on_switch_to_manual returns and why persistence failed (Store connectivity, permissions) and fix that backend issue.
  2. Make the callback raise a descriptive error instead of returning False so the root cause is visible.
  3. Retry the switch once connectivity is restored, then re-submit the pending tool actions.
  4. Fall back to keeping the current approval mode with a clear UI notice if persistence cannot be guaranteed.

Example fix

// before
def _switch_to_manual():
    store.write(mode="manual")  # returns False on failure
    return False
// after
def _switch_to_manual():
    try:
        store.write(mode="manual")
        return True
    except StoreUnavailable as exc:
        raise RuntimeError(f"could not persist manual mode: {exc}") from exc
Defensive patterns

Strategy: try-catch

Validate before calling

result = adapter._on_switch_to_manual()
result = await result if inspect.isawaitable(result) else result
if not result:
    raise StoreWriteError("manual mode persistence returned failure")

Try / catch

try:
    apply_hitl_decision(adapter, "switch_manual")
except RuntimeError as exc:
    render_turn_error(f"switch to manual failed: {exc}")
    keep_current_approval_mode()

Prevention

When it happens

Trigger: The 'switch_manual' decision path runs, the callback returns False/None (persistence rejected or Store write failed), and the turn aborts so stale approval semantics are not silently kept.

Common situations: Store outage or write rejection when switching from auto-approve to manual mid-turn; callback implementations returning False on validation failure; async callback returning an awaitable resolving to False.

Related errors


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