rust-lang/rust-analyzer · warning
cargo runnable should deserialize
Error message
cargo runnable should deserialize
What it means
The companion assertion of the cargo-runnable round-trip test: it deserializes the expected JSON back into Runnable and expects success. A panic here means the JSON fixture no longer matches the Runnable/RunnableArgs serde schema — serde_json::from_value returned a deserialization error (missing field, wrong shape, or the kind tag not selecting the Cargo variant).
Source
Thrown at crates/rust-analyzer/src/lsp/ext.rs:1009
};
let expected = json!({
"label": "cargo test -p my-crate",
"kind": "cargo",
"args": {
"environment": {"RUSTC_TOOLCHAIN": "/toolchain"},
"cwd": "/project",
"overrideCargo": null,
"workspaceRoot": "/project",
"cargoArgs": ["test", "--package", "my-crate", "--lib"],
"executableArgs": ["my_test", "--exact"],
}
});
let serialized = serde_json::to_value(&runnable).expect("serialized runnable");
assert_eq!(serialized, expected);
let deserialized: Runnable =
serde_json::from_value(expected).expect("cargo runnable should deserialize");
let RunnableArgs::Cargo(cargo) = &deserialized.args else {
panic!("expected Cargo variant, got {:?}", deserialized.args);
};
assert_eq!(cargo.cargo_args, vec!["test", "--package", "my-crate", "--lib"]);
assert_eq!(cargo.executable_args, vec!["my_test", "--exact"]);
}
#[test]
fn shell_runnable_round_trips() {
let runnable = Runnable {
label: "nextest test_one".to_owned(),
location: None,
args: RunnableArgs::Shell(ShellRunnableArgs {
environment: [("RUSTC_TOOLCHAIN".to_owned(), "/toolchain".to_owned())]
.into_iter()
.collect(),
cwd: "/project".into(),
program: "cargo".into(),View on GitHub (pinned to e8f7e90aa3)
Solutions
- Compare the fixture JSON against the current serde derive on Runnable and fix mismatched field names/shape.
- Ensure the enum uses a discriminant (the `kind` tag) so Cargo and Shell variants deserialize distinctly.
- Run with RUST_BACKTRACE=1 and print the serde error to see the exact missing/mismatched field.
- Update the expected JSON (via UPDATE_EXPECT=1 or manually) only after verifying the new schema is intended and the client extension matches.
Example fix
// before: untagged enum
#[derive(Serialize, Deserialize)]
enum RunnableArgs { Cargo(CargoRunnable), Shell(ShellRunnable) }
// after: internally tagged so variants are distinguishable
#[derive(Serialize, Deserialize)]
#[serde(tag = "kind", rename_all = "snake_case")]
enum RunnableArgs { Cargo(CargoRunnable), Shell(ShellRunnable) } Defensive patterns
Strategy: type-guard
Validate before calling
// validate fixture matches current schema before from_value
let value = serde_json::to_value(&runnable).expect("fixture must serialize");
assert!(value.get("kind").is_some(), "runnable payload must carry a kind tag"); Type guard
fn is_cargo_runnable(v: &serde_json::Value) -> bool {
v.get("kind").and_then(|k| k.as_str()) == Some("cargo")
&& v.get("cargoArgs").map(|a| a.is_array()).unwrap_or(false)
} Prevention
- Keep an explicit `kind` tag on tagged enums so variants are distinguishable.
- Update fixture JSON and struct fields together in the same commit.
- Inspect serde errors with match instead of expect to see field mismatches.
- Mirror schema changes in the VS Code client extension.
When it happens
Trigger: Changing the serde representation of RunnableArgs::Cargo (field names, tagging strategy like untagged vs internally tagged, removing/renaming cargoArgs or executableArgs) so the fixture JSON in the test fails to deserialize.
Common situations: Contributors refactoring the rust-analyzer runnable LSP extension types, accidentally switching to a tag-less enum where a shell payload parses as cargo, or renaming fields without updating the test's expected JSON.
Related errors
AI-assisted analysis of rust-lang/rust-analyzer@e8f7e90aa3 (2026-09-03).
Data as JSON: /api/errors/0bb212fa69e755b6.
Report an issue: GitHub.