Hmbown/CodeWhale · error · JsonRpcError
-32603
-32603
Error message
runtime API bridge exited before becoming ready (status {status}) What it means
The stdio app-server bridge (RuntimeBridge::start) spawns a child runtime process (the current executable, or 'codewhale' from PATH, run as 'app-server --http --host 127.0.0.1 --port <p>' with CODEWHALE_RUNTIME_TOKEN/DEEPSEEK_RUNTIME_TOKEN in its environment) and polls GET /health for up to 15 seconds. wait_until_ready() observed via try_wait() that the child had already terminated before /health ever succeeded, so the bridge is unusable; the child's exit status is embedded. It surfaces to JSON-RPC clients as internal error -32603. Note the child is spawned with stdin/stdout/stderr set to null, so its failure reason is invisible unless you run it by hand.
Source
Thrown at crates/app-server/src/lib.rs:1072
.arg(port.to_string())
.env("CODEWHALE_RUNTIME_TOKEN", auth_token)
.env("DEEPSEEK_RUNTIME_TOKEN", auth_token)
.stdin(Stdio::null())
.stdout(Stdio::null())
.stderr(Stdio::null());
if let Some(config_path) = config_path {
command.arg("--config").arg(config_path);
}
Ok(command)
}
async fn wait_until_ready(&mut self) -> Result<()> {
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,View on GitHub (pinned to 8880682c63)
Solutions
- Reproduce the child manually to see its real error: <exe> app-server --http --host 127.0.0.1 --port 18080 with CODEWHALE_RUNTIME_TOKEN=cwrt_test (its output is discarded by the bridge)
- Ensure the spawned binary is the same build as the bridge -- avoid relying on the PATH fallback to an old installed codewhale
- Validate the config path handed to RuntimeBridge::start exists and parses before spawning
- Check for port squatting on 127.0.0.1 and retry startup (the port is picked fresh each spawn)
- Drop the cached bridge (the invalidate_stdio_bridge path) so the next stdio message respawns a fresh child
Defensive patterns
Strategy: retry
Validate before calling
// Before starting the bridge, sanity-check that the child can run:
use std::process::Command;
fn runtime_child_launchable() -> bool {
let exe = std::env::current_exe()
.map(|p| p.to_string_lossy().to_string())
.unwrap_or_else(|_| "codewhale".into());
Command::new(exe)
.arg("app-server")
.arg("--help")
.stdout(std::process::Stdio::null())
.stderr(std::process::Stdio::null())
.status()
.map(|s| s.success())
.unwrap_or(false)
} Try / catch
// On -32603 'runtime API bridge exited before becoming ready': drop the cached
// bridge and let the next request respawn it (fresh port, fresh child).
match bridge.message_thread(...).await {
Ok(v) => v,
Err(err) if err.to_string().contains("exited before becoming ready") => {
invalidate_stdio_bridge(state).await; // next stdio message spawns a new child
return Err(JsonRpcError::internal(
"runtime bridge restart scheduled; retry the request".into(),
));
}
Err(err) => return Err(JsonRpcError::internal(err.to_string())),
} Prevention
- Guarantee the spawned runtime binary is the same build as the bridge (avoid the PATH 'codewhale' fallback in mixed-version installs)
- Validate any --config path exists before passing it to RuntimeBridge::start
- The child's stdio is null -- when debugging, always reproduce 'app-server --http ...' by hand to see the real startup error
When it happens
Trigger: std::env::current_exe() is unavailable and PATH resolves to an older installed 'codewhale' binary that lacks the 'app-server --http' interface or rejects the runtime-token env vars; the reserved port (bound then released by reserve_runtime_port before spawn) is retaken by another process; the optional --config path passed to the child points to a missing or invalid file; the child panics at startup (bad config, incompatible build).
Common situations: Version skew between a library build and an older codewhale binary on PATH; a stale or moved config file; tight CI/sandbox environments where the spawn or listen fails immediately.
Related errors
- Codewhale account request failed (HTTP {}, code {code})
- Codewhale account request failed (HTTP {})
- Stdio transport closed {stderr}
- ${name} is unavailable in Workflow scripts: runs must be det
- new Date()/Date() is unavailable in Workflow scripts: runs m
AI-assisted analysis of Hmbown/CodeWhale@8880682c63 (2026-08-16).
Data as JSON: /api/errors/39991cda28936f9a.
Report an issue: GitHub.