FuelLabs/sway · error

Failed to execute ps command

Error message

Failed to execute ps command

What it means

sway-lsp spawns a background tokio task that, every 60 seconds, runs `ps -p <client_pid>` to verify the editor is still alive; if the PID disappears the server exits cleanly. This expect panics when ps itself cannot be spawned (sysinfo is deliberately avoided per the fuel.nix comment). The panic kills the heartbeat task, silently stopping client monitoring — or crashes the server outright under panic=abort.

Source

Thrown at sway-lsp/src/server_state.rs:288

                        return;
                    }
                }
            }
        });
    }

    /// Spawns a new thread dedicated to checking if the client process is still active,
    /// and if not, shutting down the server.
    pub fn spawn_client_heartbeat(&self, client_pid: usize) {
        tokio::spawn(async move {
            loop {
                // Not using sysinfo here because it has compatibility issues with fuel.nix
                // https://github.com/FuelLabs/fuel.nix/issues/64
                let output = Command::new("ps")
                    .arg("-p")
                    .arg(client_pid.to_string())
                    .output()
                    .expect("Failed to execute ps command");

                if String::from_utf8_lossy(&output.stdout).contains(&format!("{client_pid} ")) {
                    tracing::trace!("Client Heartbeat: still running ({client_pid})");
                } else {
                    std::process::exit(0);
                }
                tokio::time::sleep(std::time::Duration::from_secs(60)).await;
            }
        });
    }

    /// Waits asynchronously for the `is_compiling` flag to become false.
    ///
    /// This function checks the state of `is_compiling`, and if it's true,
    /// it awaits on a notification. Once notified, it checks again, repeating
    /// this process until `is_compiling` becomes false.
    pub async fn wait_for_parsing(&self) {
        loop {

View on GitHub (pinned to 47e5e902fa)

Solutions

  1. Install procps in the environment hosting sway-lsp (`apt-get install -y procps`).
  2. Run editor and language server in the same PID namespace so `ps -p` can see the client PID.
  3. In restricted sandboxes, allow process spawning for the LSP server's seccomp/AppArmor profile.

Example fix

# before (devcontainer/Dockerfile)
FROM distroless/base   # no ps; heartbeat panics after 60s
# after
FROM debian:bookworm-slim
RUN apt-get update && apt-get install -y procps && rm -rf /var/lib/apt/lists/*
Defensive patterns

Strategy: validation

Validate before calling

# before starting the LSP in a container, confirm ps is available
command -v ps >/dev/null 2>&1 || { echo "sway-lsp heartbeat needs procps" >&2; exit 1; }

Prevention

When it happens

Trigger: The LSP server runs where /usr/bin/ps is missing (distroless/minimal containers, devcontainers without procps) or where seccomp policy blocks spawning subprocesses; heartbeat ticks 60s after initialization and panics.

Common situations: Running the Fuel LSP inside dockerized editors or CI smoke tests; hardened sandboxes that forbid fork/exec; note the sibling failure — ps works but the editor's PID is in another namespace, making the server exit prematurely.

Related errors


AI-assisted analysis of FuelLabs/sway@47e5e902fa (2026-08-16). Data as JSON: /api/errors/379657d849fadfa0. Report an issue: GitHub.