Hmbown/CodeWhale · warning

{INITIAL_PROMPT_DEFERRED_STATUS}

Error message

{INITIAL_PROMPT_DEFERRED_STATUS}

What it means

prepare_user_dispatch refuses to send a queued user message while the redaction gate is active (app.redaction_gate == true). The gate blocks dispatch until the user resolves the model-bound masking confirmation ("confirm" or "keep masking"). The error message is the INITIAL_PROMPT_DEFERRED_STATUS constant: "Initial prompt ready; complete setup to send it" — it signals the initial prompt is held, not lost.

Solutions

  1. Resolve the redaction gate prompt in the UI (choose "confirm" or "keep masking") — the deferred prompt is then sent automatically.
  2. Check app.redaction_gate before dispatch and route the message into the deferred queue instead of calling prepare_user_dispatch.
  3. In tests or scripts, simulate the gate resolution (or set app.redaction_gate = false after handling) before dispatching.
  4. The prompt is not lost: it remains queued and dispatches once setup completes; no user action beyond the gate is needed.

Example fix

// before
prepare_user_dispatch(app, config, message)?;
// after
if app.redaction_gate {
    app.status_message = Some(INITIAL_PROMPT_DEFERRED_STATUS.to_string());
    return Ok(UserDispatchPrepare::Deferred(message));
}
prepare_user_dispatch(app, config, message)?;
Defensive patterns

Strategy: validation

Validate before calling

if app.redaction_gate {
    app.status_message = Some(INITIAL_PROMPT_DEFERRED_STATUS.to_string());
    return Ok(UserDispatchPrepare::Deferred(message));
}

Type guard

fn dispatch_allowed(app: &App) -> bool { !app.redaction_gate }

Try / catch

match prepare_user_dispatch(app, config, message) {
    Err(err) if err.to_string() == INITIAL_PROMPT_DEFERRED_STATUS => {
        // expected: gate pending — keep the message queued, no user alarm
    }
    other => other?,
}

Prevention

When it happens

Trigger: Calling prepare_user_dispatch (from dispatch_user_message_with_recovery / start_user_dispatch) when app.redaction_gate is set — i.e. the first prompt was submitted before the user completed the redaction-gate confirmation flow, so the message is intentionally deferred.

Common situations: A user launching Codewhale with an initial prompt (CLI arg or restored input) while the model-bound redaction decision prompt is pending; automated tests dispatching prompts without resolving the gate.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@433685b202 (2026-09-15). Data as JSON: /api/errors/8421c47e4d5865a4. Report an issue: GitHub.

Appendix: source

Thrown at crates/tui/src/tui/ui/dispatch.rs:577

            warning: Some(
                "message_submit hook executor did not run; submission continued because no strict gate matched"
                    .to_string(),
            ),
        }
    } else {
        crate::hooks::MessageSubmitOutcome::Blocked {
            reason: "message_submit hook executor did not run; a strict gate blocked submission"
                .to_string(),
        }
    }
}

pub(crate) fn prepare_user_dispatch(
    app: &mut App,
    config: &Config,
    message: QueuedMessage,
) -> Result<UserDispatchPrepare> {
    anyhow::ensure!(!app.redaction_gate, "{INITIAL_PROMPT_DEFERRED_STATUS}");
    let _ = app.maybe_nudge_for_planning_prompt(&message.display);
    let _ = app.maybe_nudge_plugin_for_prompt(&message.display);

    // Plan paused-command changes without touching App or the engine pause
    // gate. Route selection can await and client preflight can fail; neither
    // may resume or discard a paused command unless a turn is ready to send.
    let paused_dispatch = plan_paused_command_message(app, &message.display);

    let cwd = std::env::current_dir().ok();
    // One cache for this submit: the references pass and the payload pass
    // otherwise each shell out for `@git`/`@diff`, making git compute a large
    // working-tree diff twice to attach it once (#4067 review follow-up).
    let mut git_cache = crate::tui::git_mention::GitMentionCache::default();
    let completion_index = app.composer.mention_discovery.fuzzy_candidates(
        &app.workspace,
        &app.composer.mention_cwd,
        app.mention_walk_depth,
        app.workspace_follow_symlinks,

View on GitHub (pinned to 433685b202)