clockworklabs/SpacetimeDB · warning

Module inspection cancelled

Error message

Module inspection cancelled

What it means

Inside inspect_observed, a biased tokio::select! races the inspection child process against a cancellation channel. If the result sender (send) is dropped/closed — i.e. the caller gave up — the future resolves early with 'Module inspection cancelled' and the child is killed afterwards.

Solutions

  1. Retry the schema extraction and let it finish without cancelling.
  2. Investigate what dropped/closed the channel upstream (outer timeout, dropped future, closed connection).
  3. If cancellation is expected, treat this error as benign cleanup of the inspector child process.
  4. Increase any upstream deadline so inspection is not aborted prematurely.
Defensive patterns

Strategy: try-catch

Try / catch

match result { Err(e) if e.to_string().contains("cancelled") => { /* benign: caller dropped request */ }, other => other? }

Prevention

When it happens

Trigger: The owner of the inspection request drops the oneshot sender (e.g. the outer future is aborted, caller times out upstream, or request context is cancelled) while the inspector child process is still running.

Common situations: CLI requests cancelled by the user (Ctrl-C), HTTP connections closed mid-extraction, or an outer timeout firing and dropping the inspect future.

Understand the failure class

Background: Request timed out: what client-side request timeouts mean across libraries (Request timed out, TIMED_OUT, APITimeoutError) — this error's family across 39 libraries.

Related errors


AI-assisted analysis of clockworklabs/SpacetimeDB@eddf9f5014 (2026-09-20). Data as JSON: /api/errors/2e9bbe4b0fbdb23e. Report an issue: GitHub.

Appendix: source

Thrown at crates/cli/src/schema_extract.rs:148

                .arg(&module)
                .arg("--host-type")
                .arg(host_type.to_ascii_lowercase())
                .stdin(Stdio::null())
                .stdout(Stdio::piped())
                .stderr(Stdio::null())
                .kill_on_drop(true);
            configure_inspector_environment(&mut command);
            let mut child = command.spawn().context("Cannot start local module schema inspection")?;
            observation.started(child.id());
            let mut output = Vec::new();
            let mut stdout = child
                .stdout
                .take()
                .context("Module inspection stdout unavailable")?
                .take(MAX_SCHEMA_BYTES + 1);
            let result = tokio::select! {
                biased;
                _ = send.closed() => Err(anyhow::anyhow!("Module inspection cancelled")),
                result = tokio::time::timeout(deadline, async {
                    stdout.read_to_end(&mut output).await.context("Cannot read local module schema")?;
                    ensure!(output.len() as u64 <= MAX_SCHEMA_BYTES, "Local module schema exceeds output limit");
                    let status = child.wait().await.context("Cannot reap local module inspector")?;
                    observation.reaped(status);
                    ensure!(status.success(), "Local module schema inspection failed");
                    Ok(())
                }) => result.unwrap_or_else(|_| Err(anyhow::anyhow!("Local module schema inspection timed out"))),
            };
            if result.is_err() {
                // Queue termination, then retain ownership through positive reaping.
                let _ = child.start_kill();
                let status = child
                    .wait()
                    .await
                    .context("Cannot reap failed local module inspector")?;
                observation.reaped(status);
            }

View on GitHub (pinned to eddf9f5014)