Hmbown/CodeWhale · warning · BackgroundSupervisorFailure::Disconnected

background hook supervisor is unavailable

Error message

background hook supervisor is unavailable

What it means

A background hook could not be submitted because the background hook supervisor itself is unavailable — the submission channel is disconnected (supervisor dropped, shut down, or not started). Like the queue-full case, the failure is captured in the hook result's error field instead of crashing the executor.

Solutions

  1. Restart the session/application so the hook supervisor is re-spawned.
  2. Check logs for the supervisor's crash cause (panic, startup failure) and fix it.
  3. If this occurs during shutdown, treat the lost hook as expected and move on.
  4. Verify hook supervisor initialization in configuration and that startup errors are not silently skipped.
Defensive patterns

Strategy: fallback

Try / catch

let result = run_hook_background(cmd);
if !result.success && result.error == Some("background hook supervisor is unavailable".into()) {
    log::error("hook supervisor down; running hook inline");
    run_hook_synchronously(cmd);
}

Prevention

When it happens

Trigger: Submitting a background hook when BackgroundSupervisorFailure::Disconnected is returned: the supervisor's channel send fails because the receiver side is gone (supervisor task ended or was never spawned).

Common situations: Supervisor task crashed earlier (panic in a hook runner); app shutdown racing with a final hook execution; supervisor never initialized due to an earlier startup error; hot-reload of configuration tearing down and not restoring the supervisor.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22). Data as JSON: /api/errors/7e294643a63247f6. Report an issue: GitHub.

Appendix: source

Thrown at crates/tui/src/hooks/executor.rs:2458

            plugin_authority: hook.plugin_authority.clone(),
            project_authority: hook.project_authority.clone(),
        });

        // The result describes the bounded submission, not the run: no caller
        // can mistake "queued" for "exited 0".
        HookResult {
            name: hook.name.clone(),
            background: true,
            strict: false,
            success: submission.is_ok(),
            exit_code: None,
            stdout: String::new(),
            stderr: String::new(),
            duration: started.elapsed(),
            error: submission.err().map(|failure| match failure {
                BackgroundSupervisorFailure::Full => {
                    "background hook supervisor queue is full".to_string()
                }
                BackgroundSupervisorFailure::Disconnected => {
                    "background hook supervisor is unavailable".to_string()
                }
            }),
        }
    }

    /// The timeout actually applied to a hook, foreground or background.
    ///
    /// `[hooks].default_timeout_secs` *replaces* the per-hook value when set;
    /// that is the shipped behavior and is documented as such in
    /// `docs/HOOKS.md`.
    fn effective_timeout_secs(&self, hook: &Hook) -> u64 {
        self.config.effective_timeout_secs(hook)
    }
}

/// Classify a tool call for `condition = { type = "tool_category", … }`.

View on GitHub (pinned to 73e0f67d83)