Hmbown/CodeWhale · critical

reviewed plugin stage changed before stdio launch

Error message

reviewed plugin stage changed before stdio launch

What it means

TOCTOU guard at stdio launch: the staged manifest is re-validated and its content_hash and capability_hash are compared against the hashes recorded when the plugin was reviewed; any mismatch aborts the launch. It fires when plugin files changed on disk after review - tampering, but also legitimate edits or version bumps that bypassed the review flow. The reviewed approval never covers the changed bytes.

Source

Thrown at crates/tui/src/mcp.rs:667

        server_name: &str,
        command: &str,
        args: &[String],
        cwd: Option<&Path>,
    ) -> Result<ReviewedStdioLaunch> {
        self.validate_before_stdio_spawn(server_name)?;
        let staged_root = self
            .authority
            .staged_manifest
            .parent()
            .context("reviewed plugin stage manifest has no parent")?;
        let validated = crate::plugins::manifest::PluginManifest::validate_from_path(
            &self.authority.staged_manifest,
        )
        .map_err(|_| anyhow::anyhow!("reviewed plugin stage could not be opened for launch"))?;
        if validated.content_hash != self.authority.content_hash
            || validated.capability_hash != self.authority.capability_hash
        {
            anyhow::bail!("reviewed plugin stage changed before stdio launch");
        }

        let mut launch = ReviewedStdioLaunch {
            command: std::ffi::OsString::from(command),
            args: args.iter().map(std::ffi::OsString::from).collect(),
            cwd: cwd.map(Path::to_path_buf),
            opened_files: Vec::new(),
            #[cfg(unix)]
            cwd_fd: None,
        };
        if Path::new(command).is_absolute() {
            launch.bind_command(staged_root, Path::new(command), &validated.file_hashes)?;
        }
        for (index, argument) in args.iter().enumerate() {
            let path = Path::new(argument);
            if path.is_absolute() && path.starts_with(staged_root) && path.is_file() {
                launch.args[index] = launch.bind_file(staged_root, path, &validated.file_hashes)?;
            }

View on GitHub (pinned to 8880682c63)

Solutions

  1. If the change is intentional: /plugin reload, inspect /plugin show <name>, repeat the displayed trust command, then /plugin enable <name> so the new hashes are reviewed
  2. Stop editing staged or bundled files in place; publish a new bundle version instead
  3. If nothing was changed by you, audit what modified the stage (mtimes, audit logs) before re-trusting - treat it as possible tampering

Example fix

# error: reviewed plugin stage changed before stdio launch
/plugin reload
/plugin show my-plugin   # confirm the new hashes are expected
# repeat the displayed trust command, then:
/plugin enable my-plugin
Defensive patterns

Strategy: retry

Validate before calling

// Detect drift before launch: re-hash the stage and compare with the review record
fn stage_matches(review: &ReviewRecord, staged: &std::path::Path) -> bool {
    PluginManifest::validate_from_path(staged)
        .map(|v| v.content_hash == review.content_hash && v.capability_hash == review.capability_hash)
        .unwrap_or(false)
}

Try / catch

// Never bypass: restage + re-review is the only sanctioned recovery
if err.to_string().contains("changed before stdio launch") {
    reload_and_retrust(&plugin)?; // /plugin reload + trust + /plugin enable
    return retry_launch_once();
}

Prevention

When it happens

Trigger: Editing files inside the plugin stage or reviewed source between approval and launch, an update overwriting the bundle without redoing trust, external processes (sync tools, CI deploys) touching staged files, or deliberate tampering.

Common situations: Developers editing plugin sources in place, CI redeploying bundles under running sessions, dropbox-style sync touching staged files, supply-chain style attempts to swap code after review.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@8880682c63 (2026-08-16). Data as JSON: /api/errors/18fd4457fd300723. Report an issue: GitHub.