{"record":{"id":"51648ac44da122c7","repo":"jlcodes99/cockpit-tools","slug":"wakeupgateway-ls-languageserverstarted-51648a","errorCode":null,"errorMessage":"[WakeupGateway] 官方 LS LanguageServerStarted 解析失败: {}","messagePattern":"\\[WakeupGateway\\] 官方 LS LanguageServerStarted 解析失败: (.+?)","errorType":"http","errorClass":null,"httpStatus":400,"severity":"warning","filePath":"src-tauri/src/modules/wakeup_gateway.rs","lineNumber":1252,"sourceCode":"            \"text/plain; charset=utf-8\",\n        ));\n    }\n\n    if path_matches_rpc_method(&path, \"LanguageServerStarted\") {\n        match parse_official_ls_started_request(&parsed.body) {\n            Ok(started) => {\n                if let Ok(mut guard) = state.started_sender.lock() {\n                    if let Some(tx) = guard.take() {\n                        let _ = tx.send(started);\n                    }\n                }\n                return OfficialLsExtensionAction::Close(extension_unary_response(\n                    &content_type,\n                    &[],\n                ));\n            }\n            Err(err) => {\n                crate::modules::logger::log_error(&format!(\n                    \"[WakeupGateway] 官方 LS LanguageServerStarted 解析失败: {}\",\n                    err\n                ));\n                return OfficialLsExtensionAction::Close(text_response(\n                    400,\n                    \"Bad Request\",\n                    &err,\n                    \"text/plain; charset=utf-8\",\n                ));\n            }\n        }\n    }\n\n    if path_matches_rpc_method(&path, \"SubscribeToUnifiedStateSyncTopic\") {\n        let topic = match parse_subscribe_topic_from_connect_body(&parsed.body) {\n            Ok(v) => v,\n            Err(err) => {\n                crate::modules::logger::log_error(&format!(","sourceCodeStart":1234,"sourceCodeEnd":1270,"githubUrl":"https://github.com/jlcodes99/cockpit-tools/blob/1ed8b77992d62ca81fabf744deb0839ad361d5bf/src-tauri/src/modules/wakeup_gateway.rs#L1234-L1270","documentation":"The gateway module implements a local mock of the official LS's Connect RPC surface. When an incoming POST matches the LanguageServerStarted RPC method, route_official_ls_extension_request calls parse_official_ls_started_request on the request body; if protobuf parsing fails it logs '官方 LS LanguageServerStarted 解析失败: {}' and returns HTTP 400 Bad Request with the parse error. This happens because the incoming Connect/proto envelope did not decode into the expected LanguageServerStarted message.","triggerScenarios":"An official LS (or any client) POSTs to the local extension server's LanguageServerStarted endpoint and parse_official_ls_started_request returns Err: malformed or truncated protobuf body, wrong Connect envelope framing (unary vs streaming content-type), empty body, or a proto schema version whose field layout no longer matches the parser.","commonSituations":"Official LS version upgrade changed the LanguageServerStarted proto definition; a client sends application/json where application/proto is expected (or vice versa) so the bytes are not valid protobuf; proxy or middleware corrupts/truncates the request body; a security scanner or wrong client hits the local port with garbage bytes.","solutions":["Log and inspect the raw request bytes and content-type header alongside the parse error to confirm whether the body is protobuf at all.","Update parse_official_ls_started_request to match the proto schema of the installed official LS version (compare against the LS's bundled .proto definitions).","Reject unsupported content-types early with a clear 415-style response instead of attempting protobuf decode on JSON or form bodies.","Verify the Connect envelope framing: for application/connect+proto the body is length-prefixed messages; strip the envelope before decoding the inner message.","Capture the offending payload (bounded hex/log dump) for a bug report if the sender is a legitimate LS build."],"exampleFix":"// before: parse regardless of content-type\nmatch parse_official_ls_started_request(&parsed.body) { ... }\n// after: guard content-type and log offending bytes on failure\nif !content_type.contains(\"proto\") {\n    return Close(text_response(415, \"Unsupported Media Type\", \"expected application/proto\", \"text/plain; charset=utf-8\"));\n}\nmatch parse_official_ls_started_request(&parsed.body) {\n    Ok(started) => ...,\n    Err(err) => {\n        log_error(&format!(\"LanguageServerStarted 解析失败: {} (len={}, head={:?})\", err, parsed.body.len(), &parsed.body[..parsed.body.len().min(32)]));\n        Close(text_response(400, \"Bad Request\", &err, \"text/plain; charset=utf-8\"))\n    }\n}","handlingStrategy":"validation","validationCode":"// Validate before parsing: content-type and non-empty protobuf-ish body\nfn plausibly_proto(body: &[u8], content_type: &str) -> bool {\n    content_type.to_ascii_lowercase().contains(\"proto\") && !body.is_empty()\n}","typeGuard":null,"tryCatchPattern":"// Server-side: never panic on bad input; respond 400 and keep serving\nmatch parse_official_ls_started_request(&parsed.body) {\n    Ok(started) => { /* forward to started_sender */ }\n    Err(err) => {\n        log_error(&format!(\"LanguageServerStarted 解析失败: {}\", err));\n        return OfficialLsExtensionAction::Close(text_response(400, \"Bad Request\", &err, \"text/plain; charset=utf-8\"));\n    }\n}","preventionTips":["Check the content-type header before protobuf decode and reject JSON/plain bodies with 415.","Keep the proto parser in sync with the official LS version; add a unit test per supported LS version.","Log a bounded hex dump of bodies that fail to parse so upgrades that change the schema are diagnosable.","Firewall the local extension port to loopback-only clients to avoid garbage traffic."],"tags":["protobuf","parsing","grpc","connect-rpc","language-server","rust"],"backgroundTag":"protobuf-parse-failed","analyzedSha":"1ed8b77992d62ca81fabf744deb0839ad361d5bf","analyzedAt":"2026-09-05T09:51:41.178Z","contentChangedAt":"2026-09-05T09:51:41.178Z","schemaVersion":2},"datasetVersion":"2026-09-12T12:17:11.808Z"}