{"record":{"id":"4f8d299c60c49a60","repo":"facebook/flow","slug":"type-at-pos-server-payload-is-valid-json","errorCode":null,"errorMessage":"type-at-pos: server payload is valid JSON","messagePattern":"type-at-pos: server payload is valid JSON","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"rust_port/crates/flow_cli/src/type_at_pos_command.rs","lineNumber":163,"sourceCode":"    file_contents: Option<String>,\n    pretty: bool,\n    strip_root: Option<&str>,\n    response: server_prot::response::infer_type::T,\n) {\n    let server_prot::response::infer_type::T {\n        loc,\n        tys,\n        refining_locs,\n        refinement_invalidated: _,\n        documentation,\n    } = response;\n    match tys {\n        server_prot::response::infer_type::Payload::Json(types) => {\n            // Wrap the server's pre-serialized \"types\" JSON as a `RawValue` so the\n            // duplicate keys produced by `ty_debug::json_of_utility` survive the\n            // round-trip — `serde_json::Map` would deduplicate them.\n            let types_raw = serde_json::value::RawValue::from_string(types)\n                .expect(\"type-at-pos: server payload is valid JSON\");\n            let offset_table = file_contents\n                .as_deref()\n                .map(flow_parser::offset_utils::OffsetTable::make);\n            let loc_json =\n                flow_common::reason::json_of_loc(strip_root, false, offset_table.as_ref(), &loc);\n            let deprecated = error_utils::deprecated_json_props_of_loc(strip_root, &loc);\n            // Fields are declared in alphabetical order so the serde-derived\n            // serializer emits them in the same order OCaml's `Hh_json`\n            // (`sort_keys=true`) does.\n            #[derive(serde::Serialize)]\n            struct OuterResponse<'a> {\n                #[serde(skip_serializing_if = \"Option::is_none\")]\n                documentation: Option<&'a str>,\n                end: &'a serde_json::Value,\n                endline: &'a serde_json::Value,\n                line: &'a serde_json::Value,\n                loc: &'a serde_json::Value,\n                path: &'a serde_json::Value,","sourceCodeStart":145,"sourceCodeEnd":181,"githubUrl":"https://github.com/facebook/flow/blob/f88ac94bcf6992f5d5a158854d94613ebb92c6e6/rust_port/crates/flow_cli/src/type_at_pos_command.rs#L145-L181","documentation":"`flow type-at-pos` asks the flow server for the types at a position; the server returns an already-serialized JSON string that the client wraps with `serde_json::value::RawValue::from_string` so duplicate keys survive. This expect panics when the server's `types` payload is not strictly valid JSON — i.e., the server-side serializer (`ty_debug::json_of_utility`) emitted malformed output such as bare NaN/Infinity, trailing data, or bad escapes. Duplicate keys are legal here; invalid syntax is not.","triggerScenarios":"A version-skewed server whose type serializer differs from the client's expectations; a server bug where some type node serializes with invalid JSON syntax; a truncated payload because the server crashed or the connection dropped mid-response; a custom/mock server returning hand-built strings.","commonSituations":"A stale flow server still running after the client binary was upgraded (build-id check not hit yet); running against a patched or instrumented server build; local development changes to the server-side JSON emitter that were not round-trip tested.","solutions":["Stop the stale server (`flow stop`, or remove the flow temp dir) so a fresh server from the same build as the client starts, then retry.","Verify client and server come from the same build/version before querying type-at-pos.","Capture the raw payload (enable logging on the client) and validate it with `jq` or `serde_json::from_str::<serde_json::Value>` to identify the malformed fragment; fix the server-side `json_of_utility` path.","If you patched the server serializer, add a round-trip test through serde_json so invalid output is caught server-side."],"exampleFix":"// before\nlet types_raw = serde_json::value::RawValue::from_string(types)\n    .expect(\"type-at-pos: server payload is valid JSON\");\n\n// after\nlet types_raw = match serde_json::value::RawValue::from_string(types) {\n    Ok(v) => v,\n    Err(e) => {\n        eprintln!(\"type-at-pos: server sent invalid JSON ({e}); restart the flow server and retry\");\n        std::process::exit(1);\n    }\n};","handlingStrategy":"validation","validationCode":"// Validate the server payload before wrapping it as a RawValue\nfn payload_is_valid_json(types: &str) -> bool {\n    serde_json::from_str::<serde_json::Value>(types).is_ok()\n}","typeGuard":null,"tryCatchPattern":"match serde_json::value::RawValue::from_string(types) {\n    Ok(raw) => raw,\n    Err(e) => {\n        eprintln!(\"server sent invalid JSON ({e}); restart the flow server and retry\");\n        std::process::exit(1);\n    }\n}","preventionTips":["Keep client and server binaries on the same build; stop stale servers after upgrades before querying type-at-pos.","Round-trip test any change to the server-side JSON emitter through serde_json.","Never feed hand-built strings into RawValue without strict JSON validation."],"tags":["json","serialization","server-client","type-at-pos"],"backgroundTag":"json-parse-error","analyzedSha":"f88ac94bcf6992f5d5a158854d94613ebb92c6e6","analyzedAt":"2026-08-20T10:41:37.992Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}