A dependency bump can break a target you never even build for yourself.
Yesterday’s SciRS2 0.6.3 landed seven Windows-compatibility fixes across the workspace. Today we released SciRS2 0.6.4 — a fast follow-up that fixes something 0.6.3’s own release process caught in the act: the oxifft 0.4.1 bump silently broke wasm32-unknown-unknown builds of scirs2-fft and scirs2-signal, which in turn blocked scirs2-wasm and the scirs2 meta-crate from publishing at all.
SciRS2 stays what it has always been: no C, no Fortran, no CUDA Toolkit, no system BLAS — a single static binary (or WASM module) that compiles and runs everywhere. 0.6.4 makes that “everywhere” whole again for the WASM target specifically.
Why this bug was easy to miss
oxifft 0.4.1 added a hard fail-fast guard: if its threading feature (backed by rayon) is active while compiling for wasm32-unknown-unknown, it now refuses to compile at all instead of shipping code that would panic on its first thread spawn at runtime. That guard is correct and welcome — but scirs2-fft and scirs2-signal both pulled in oxifft with full default features and no target-specific override, so the guard tripped the instant anything tried to build either crate for wasm32.
Nobody building scirs2-fft or scirs2-signal for a normal (non-WASM) target ever saw this — the bug is invisible unless you specifically target wasm32-unknown-unknown, which scirs2-wasm’s own .cargo/config.toml does automatically. cargo publish’s verification build caught it immediately, but by the time the fix was ready, scirs2-fft and scirs2-signal had already published their 0.6.3 artifacts to crates.io — immutably, without the fix. scirs2-wasm and scirs2 (which depends on it as an optional feature) had to sit out 0.6.3 entirely.
Technical Deep Dive: target-gating a dependency’s features
The fix is a general-purpose pattern worth knowing if you ship a crate that’s built for both native and wasm32 targets:
# Before: oxifft's own defaults (std + threading) apply unconditionally,
# even when this crate is compiled for wasm32.
[dependencies]
oxifft = { version = "0.4.1", optional = true }
# After: split the dependency by target so wasm32 never sees `threading`.
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
oxifft = { version = "0.4.1", optional = true }
[target.'cfg(target_arch = "wasm32")'.dependencies]
oxifft = { version = "0.4.1", optional = true, default-features = false, features = ["std"] }
One Cargo quirk we hit while writing this: { workspace = true, default-features = false } is silently not honored inside a [target.'cfg(...)'.dependencies] table — the crate’s real defaults stayed active regardless of the override. Switching to a literal version = "0.4.1" (dropping workspace = true for just that one platform-gated entry) made default-features = false take effect immediately. If a per-target dependency override ever looks like it’s being silently ignored, that’s the first thing to check.
Getting Started
scirs2-wasm and scirs2 are back on crates.io at 0.6.4 — no change needed on your end beyond bumping the version:
cargo add scirs2-wasm
[dependencies]
scirs2-wasm = "0.6.4"
What’s New in 0.6.4
Fixed
scirs2-fft/scirs2-signal:oxifft’sthreading(rayon) feature is no longer enabled by default onwasm32-unknown-unknown— both crates now declareoxifftvia target-gated dependency tables.
Published
scirs2-wasmandscirs2(the meta-crate) resume publishing at 0.6.4, catching up after being skipped at 0.6.3. All 33 workspace crates are now uniformly at 0.6.4.
See CHANGELOG.md [0.6.4] for the complete list.
Tips
- If you maintain a crate that supports both native and
wasm32targets, audit every dependency’s default features for thread-pool assumptions.rayon, and anything built on it, is a common source of exactly this class of silent wasm32 breakage. - Split a dependency by target with
[target.'cfg(...)'.dependencies]rather than trying to disable a feature globally when only one target needs the narrower feature set — this keeps full performance on native builds while still compiling cleanly for WASM. - If a per-target
default-features = falseoverride seems to have no effect, stop usingworkspace = truefor that specific entry. A literalversion = "..."in the target-gated table is the reliable form. - Re-run
cargo publish --dry-runfor the exact target you ship, not just your default host target, if your crate has a.cargo/config.tomlthat pins a non-default build target (asscirs2-wasmdoes forwasm32-unknown-unknown) — the defaultcargo publishverification will otherwise miss target-specific breakage entirely.
This is the foundation
SciRS2 0.6.4 keeps the sovereign scientific-computing layer of the COOLJAPAN ecosystem whole across every target it claims to support, WASM included:
- NumRS2 — NumPy-compatible N-dimensional arrays in pure Rust.
- PandRS — Pandas-compatible DataFrames.
- OptiRS — advanced ML optimizers extending SciRS2.
- ToRSh — a PyTorch-compatible deep-learning framework.
- SkleaRS — a scikit-learn-compatible ML library.
- TrustformeRS — Hugging Face Transformers in pure Rust.
Repository: https://github.com/cool-japan/scirs
Star the repo if you’d rather a wasm32 regression get caught by a publish pipeline than by a user’s browser console.
The era of “it builds on my machine” is over. Pure Rust scientific computing is here — fast, safe, and correct on every target it ships to, browser included.
— KitaSan at COOLJAPAN OÜ July 28, 2026