rust-lang/rust-analyzer · warning

shell runnable should deserialize

Error message

shell runnable should deserialize

What it means

The shell-runnable deserialization half of the round-trip test: from_value(expected) must produce RunnableArgs::Shell. A panic means the fixture JSON does not deserialize under the current serde schema — typically because the `kind` tag is missing or renamed, letting the payload be misread as (or fail as) a Cargo variant.

Source

Thrown at crates/rust-analyzer/src/lsp/ext.rs:1048

        };
        let expected = json!({
            "label": "nextest test_one",
            "kind": "shell",
            "args": {
                "environment": {"RUSTC_TOOLCHAIN": "/toolchain"},
                "cwd": "/project",
                "program": "cargo",
                "args": ["nextest", "run", "--package", "my-crate"],
            }
        });

        let serialized = serde_json::to_value(&runnable).expect("serialized runnable");
        assert_eq!(serialized, expected);

        // Every shell runnable is a structurally valid cargo runnable if the `kind` tag isn't
        // used. This test ensures that the `kind` tag is used.
        let deserialized: Runnable =
            serde_json::from_value(expected).expect("shell runnable should deserialize");
        let RunnableArgs::Shell(shell) = &deserialized.args else {
            panic!("expected Shell variant, got {:?}", deserialized.args);
        };
        assert_eq!(shell.program, "cargo");
        assert_eq!(shell.args, vec!["nextest", "run", "--package", "my-crate"]);
    }
}

View on GitHub (pinned to e8f7e90aa3)

Solutions

  1. Restore/keep the internally-tagged representation: #[serde(tag = "kind")] with a Shell discriminant.
  2. Sync the fixture JSON field names with the current struct field names (program, args, cwd, etc.).
  3. Read the serde error via RUST_BACKTRACE=1 or by replacing expect with match to see the exact mismatch.
  4. Regenerate expectations with UPDATE_EXPECT=1 only after confirming the new schema, and mirror it in the client extension.

Example fix

// before: fixture without discriminant
{ "cwd": "/project", "program": "cargo", "args": ["nextest"] }
// after: include the kind tag the schema requires
{ "kind": "shell", "cwd": "/project", "program": "cargo", "args": ["nextest"] }
Defensive patterns

Strategy: type-guard

Validate before calling

// ensure the fixture selects the Shell variant before deserializing
assert_eq!(expected.get("kind").and_then(|k| k.as_str()), Some("shell"),
    "shell runnable fixture must use the kind tag");

Type guard

fn is_shell_runnable(v: &serde_json::Value) -> bool {
    v.get("kind").and_then(|k| k.as_str()) == Some("shell")
        && v.get("program").map(|p| p.is_string()).unwrap_or(false)
}

Prevention

When it happens

Trigger: Removing or renaming the `kind` tag in the Runnable serde representation, renaming program/args fields without updating the fixture, or switching the enum tagging strategy so the shell JSON no longer selects RunnableArgs::Shell.

Common situations: Refactoring the lsp_ext Runnable types, new contributors unfamiliar with the test's stated invariant (every shell runnable must be distinguishable from a cargo runnable only via the `kind` tag).

Related errors


AI-assisted analysis of rust-lang/rust-analyzer@e8f7e90aa3 (2026-09-03). Data as JSON: /api/errors/415837a87b660c1b. Report an issue: GitHub.