shadowsocks/shadowsocks-rust · critical

create tokio Runtime

Error message

create tokio Runtime

What it means

shadowsocks-rust's `create` builds a tokio multi-thread (or current-thread) Runtime via `builder.enable_all().build().expect("create tokio Runtime")`. The expect panics when tokio cannot construct the runtime, which almost always means the requested worker/thread configuration is invalid or runtime resources (threads) cannot be allocated. This happens before any server work starts, so the process aborts immediately during startup.

Source

Thrown at src/service/local.rs:996

            })?;
        }

        info!("shadowsocks local {} build {}", crate::VERSION, crate::BUILD_TIME);

        let mut builder = match service_config.runtime.mode {
            RuntimeMode::SingleThread => Builder::new_current_thread(),
            #[cfg(feature = "multi-threaded")]
            RuntimeMode::MultiThread => {
                let mut builder = Builder::new_multi_thread();
                if let Some(worker_threads) = service_config.runtime.worker_count {
                    builder.worker_threads(worker_threads);
                }

                builder
            }
        };

        let runtime = builder.enable_all().build().expect("create tokio Runtime");

        (config, service_config, runtime)
    };

    let main_fut = async move {
        let config_path = config.config_path.clone();

        let instance = Server::new(config).await.expect("create local");

        let reload_task = match config_path {
            Some(config_path) => ServerReloader {
                config_path: config_path.clone(),
                balancer: instance.server_balancer().clone(),
            }
            .launch_reload_server_task()
            .boxed(),
            None => future::pending().boxed(),
        };

View on GitHub (pinned to 8eb0f0a65b)

Solutions

  1. Fix the `--worker-threads` (or equivalent builder config) value — must be >= 1
  2. Remove explicit runtime tuning flags so tokio defaults are used
  3. Check container/cgroup thread and pid limits (ulimit -u, pids.max) and raise them
  4. Upgrade shadowsocks-rust / tokio if running on an unusual platform

Example fix

// before
shadowsocks-service --worker-threads 0 ...
// after
shadowsocks-service --worker-threads 4 ...
Defensive patterns

Strategy: validation

Validate before calling

// before launch
let workers: usize = std::env::args().collect::<Vec<_>>().join(" ")
    .split("--worker-threads")
    .nth(1).and_then(|s| s.trim().split_whitespace().next())
    .and_then(|v| v.parse().ok()).unwrap_or(num_cpus::get());
assert!(workers >= 1, "--worker-threads must be >= 1");

Type guard

fn valid_workers(n: u64) -> bool { n >= 1 }

Try / catch

// panic from .expect is not catchable in a meaningful way; validate before launch
match std::panic::catch_unwind(start_service) {
    Ok(_) => {},
    Err(_) => eprintln!("runtime init failed; check --worker-threads and thread limits"),
}

Prevention

When it happens

Trigger: Calling `create` (invoked from `main`) with a `--worker-threads` value of 0, or with thread/stack configuration values tokio's Builder rejects; tokio's `Builder::build()` returning Err (e.g. io error spawning threads).

Common situations: Users passing `--worker-threads 0` on the command line; containers with extremely low thread limits (ulimit / cgroup pids restriction); exotic platforms where tokio cannot spawn threads.

Related errors


AI-assisted analysis of shadowsocks/shadowsocks-rust@8eb0f0a65b (2026-09-09). Data as JSON: /api/errors/63f2fac8e3aa23c8. Report an issue: GitHub.