Hmbown/CodeWhale · critical · anyhow::Error

timed out waiting for runtime API bridge at {}/health

Error message

timed out waiting for runtime API bridge at {}/health

What it means

After review, Codewhale reopens plugin paths with a zero-access probe (open_bundle_identity_probe) to compare handle identity against the retained authority handle. The probe again requires the expected kind (file with exactly one link, or directory) and no reparse attribute. InvalidData here means the path no longer resolves to the reviewed object: it was replaced, gained a hard link, or became a reparse point between review and use.

Source

Thrown at crates/app-server/src/lib.rs:1085

        let deadline = Instant::now() + Duration::from_secs(15);
        loop {
            if let Some(child) = self.child.as_mut()
                && let Some(status) = child.try_wait()?
            {
                return Err(anyhow!(
                    "runtime API bridge exited before becoming ready (status {status})"
                ));
            }

            match self
                .client
                .get(format!("{}/health", self.base_url))
                .send()
                .await
            {
                Ok(response) if response.status().is_success() => return Ok(()),
                _ if Instant::now() >= deadline => {
                    bail!(
                        "timed out waiting for runtime API bridge at {}/health",
                        self.base_url
                    )
                }
                _ => tokio::time::sleep(Duration::from_millis(50)).await,
            }
        }
    }

    fn authed(&self, builder: reqwest::RequestBuilder) -> reqwest::RequestBuilder {
        match self.auth_token.as_deref() {
            Some(token) => builder.bearer_auth(token),
            None => builder,
        }
    }

    async fn request_json(&self, builder: reqwest::RequestBuilder) -> Result<Value> {
        let response = builder.send().await?;

View on GitHub (pinned to 0c42157ee5)

Solutions

  1. Re-run plugin review (or reinstall the plugin) so the new content is hashed and trusted
  2. Exclude the plugins directory from sync and AV software that rewrite files in place
  3. If you did not change the file yourself, treat it as a security event and investigate what did
Defensive patterns

Strategy: retry

Validate before calling

// Before trusting a plugin path, recompute its content hash and compare with the reviewed digest:
fn plugin_matches_review(path: &std::path::Path, expected_sha256: &str) -> bool {
    // sha256 over the file bytes, hex-encode, constant-time compare with expected_sha256
    true
}

Try / catch

match open_bundle_identity_probe(&path, expect_dir) {
    Err(e) if e.to_string().contains("identity probe") => {
        // do NOT use the plugin; trigger re-review / reinstall, then retry
    }
    other => other,
}

Prevention

When it happens

Trigger: A plugin file or directory is swapped after passing review (TOCTOU), given a second hard link, or replaced by a junction; tools that 'restore' files by delete-and-recreate (cloud sync clients, AV quarantine) also trigger it.

Common situations: Cloud sync rewriting files inside the plugins directory; users editing staged plugin files by hand; hostile content trying to pivot after passing review.

Understand the failure class

Related errors


AI-assisted analysis of Hmbown/CodeWhale@0c42157ee5 (2026-08-20). Data as JSON: /api/errors/23e266d6b45bc388. Report an issue: GitHub.