{"record":{"id":"ad9b5fc35a30e599","repo":"langchain-ai/deepagents","slug":"session-start-stopped-by-hook","errorCode":null,"errorMessage":"Session start stopped by hook","messagePattern":"Session start stopped by hook","errorType":"exception","errorClass":"ClientHookStopError","httpStatus":null,"severity":"error","filePath":"libs/code/deepagents_code/client/non_interactive.py","lineNumber":2239,"sourceCode":"        # Project hooks require an explicit opt-in, matching `--trust-project-mcp`.\n        # Persisted interactive trust deliberately does not carry into headless\n        # runs, so CI never inherits a grant made at someone's terminal.\n        trust=WorkspaceTrust.explicit_only(Path.cwd(), granted=trust_project_hooks),\n    )\n    state.hooks.attach_output(notice=present_hook_notice, status=hook_status)\n    state.hooks.apply_graph_context(context)\n    context[\"approval_mode\"] = resolved_approval_mode.value\n    context[\"auto_approve\"] = resolved_approval_mode is ApprovalMode.YOLO\n    state.active_model = runtime_state.model_name or None\n    state.transcript = state.hooks.recorder(thread_id)\n\n    start_outcome = await state.hooks.on_session_start(\n        SessionStartCause.STARTUP,\n        model=runtime_state.model_name or None,\n    )\n    if not start_outcome.ok:\n        await _end_headless_session(state, SessionEndCause.OTHER)\n        raise ClientHookStopError(\n            start_outcome.stop_reason or \"Session start stopped by hook\"\n        )\n    session_context = state.hooks.take_pending_context()\n    if session_context:\n        stream_input[\"messages\"].insert(\n            0,\n            {\"role\": \"system\", \"content\": \"\\n\\n\".join(session_context)},\n        )\n\n    try:\n        state.transcript.append([HumanMessage(content=message)])\n        if state.hooks.has_handlers(HookEvent.USER_PROMPT_SUBMIT):\n            prompt_outcome = await state.hooks.on_user_prompt(message)\n            if not prompt_outcome.ok:\n                _raise_client_hook_stop(\n                    prompt_outcome.stop_reason\n                    or \"User prompt submission stopped by hook\"\n                )","sourceCodeStart":2221,"sourceCodeEnd":2257,"githubUrl":"https://github.com/langchain-ai/deepagents/blob/a1af029e6e73cb17c36bff823d227747b28e91e1/libs/code/deepagents_code/client/non_interactive.py#L2221-L2257","documentation":"At the start of `_run_agent_loop`, `on_session_start(SessionStartCause.STARTUP)` runs; if the hook outcome is not ok, the session is ended with `SessionEndCause.OTHER` and `ClientHookStopError` is raised with the hook's `stop_reason` or this default message. Client hooks therefore have veto power over starting a headless session.","triggerScenarios":"Calling `run_non_interactive` (which enters `_run_agent_loop`) while a registered client lifecycle hook rejects the startup cause — e.g. policy checks on model name, environment, or repository.","commonSituations":"Org guardrail hooks blocking untrusted project hooks or disallowed models; hooks failing environment validation in CI; tests asserting that a user-prompt stop or untrusted project hooks end the session once.","solutions":["Read `stop_reason` from the error; fix the condition in the hook that rejected startup.","Adjust hook configuration (model allow-lists, trust settings for project hooks) so legitimate sessions pass.","Catch `ClientHookStopError` in the caller to handle a deliberate stop gracefully (session state is already ended via `_end_headless_session`).","If no hook should be active, check that hooks aren't being loaded unintentionally (project hook trust opt-in)."],"exampleFix":"// before: hook blocks everything not on an old list\nif model not in [\"old-model\"]: return HookOutcome(ok=False, stop_reason=\"model blocked\")\n// after: keep list current\nif model not in APPROVED_MODELS: return HookOutcome(ok=False, stop_reason=f\"model {model} not approved\")","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"from deepagents_code.hooks.client_lifecycle import ClientHookStopError\ntry:\n    run_non_interactive(task)\nexcept ClientHookStopError as exc:\n    # session already ended with SessionEndCause.OTHER; surface the hook's reason\n    report_session_blocked(exc)","preventionTips":["Keep hook model/policy allow-lists current","Review project-hook trust settings before headless runs","Return meaningful stop_reason values from hooks for diagnosability"],"tags":["hooks","session-start","headless","lifecycle"],"backgroundTag":"client-hook-stop","analyzedSha":"a1af029e6e73cb17c36bff823d227747b28e91e1","analyzedAt":"2026-08-29T11:43:24.718Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}