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
- Restore/keep the internally-tagged representation: #[serde(tag = "kind")] with a Shell discriminant.
- Sync the fixture JSON field names with the current struct field names (program, args, cwd, etc.).
- Read the serde error via RUST_BACKTRACE=1 or by replacing expect with match to see the exact mismatch.
- 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
- Never remove or rename the `kind` discriminant on RunnableArgs.
- Keep test fixtures in lockstep with struct field names.
- Read the serde error message before rewriting expectations.
- Run UPDATE_EXPECT=1 only after manually reviewing the diff.
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.