{"record":{"id":"9ac340e9f780fb80","repo":"jlcodes99/cockpit-tools","slug":"wakeupgateway-ls-subscribetounifiedstatesynct-9ac340","errorCode":null,"errorMessage":"[WakeupGateway] 官方 LS SubscribeToUnifiedStateSyncTopic 解析失败: {}","messagePattern":"\\[WakeupGateway\\] 官方 LS SubscribeToUnifiedStateSyncTopic 解析失败: (.+?)","errorType":"http","errorClass":null,"httpStatus":400,"severity":"warning","filePath":"src-tauri/src/modules/wakeup_gateway.rs","lineNumber":1270,"sourceCode":"                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!(\n                    \"[WakeupGateway] 官方 LS SubscribeToUnifiedStateSyncTopic 解析失败: {}\",\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        let topic_bytes = match topic.as_str() {\n            \"uss-oauth\" => &state.uss_oauth_topic_bytes,\n            \"uss-enterprisePreferences\" | \"uss-agentPreferences\" => &state.empty_topic_bytes,\n            _ => &state.empty_topic_bytes,\n        };\n        let update = build_unified_state_sync_update_initial_state(topic_bytes);","sourceCodeStart":1252,"sourceCodeEnd":1288,"githubUrl":"https://github.com/jlcodes99/cockpit-tools/blob/1ed8b77992d62ca81fabf744deb0839ad361d5bf/src-tauri/src/modules/wakeup_gateway.rs#L1252-L1288","documentation":"For POSTs matching the SubscribeToUnifiedStateSyncTopic RPC, the local official-LS extension server parses the topic name out of the Connect request body via parse_subscribe_topic_from_connect_body. On failure it logs '官方 LS SubscribeToUnifiedStateSyncTopic 解析失败: {}' and returns HTTP 400 Bad Request, closing the stream. The parser must extract the topic string (e.g. uss-oauth) from the protobuf-encoded body, so the error means the body did not contain a decodable topic field.","triggerScenarios":"A client (official LS) subscribes to the unified state sync topic and parse_subscribe_topic_from_connect_body returns Err: the Connect body is empty, the protobuf field holding the topic string is missing or at an unexpected field number/wire type, the envelope framing was stripped incorrectly, or a proto schema change altered the subscription message layout.","commonSituations":"Official LS upgrade changes the SubscribeToUnifiedStateSyncTopic request proto so the topic field number shifts; client sends a streaming-framed body where the parser expects a single unary message (or vice versa); truncated body from a connection reset mid-request; another process probing the port with non-proto bytes.","solutions":["Log the raw body length and first bytes with the parse error to see whether an envelope was stripped or the body is empty.","Update parse_subscribe_topic_from_connect_body to the field number/type used by the installed LS version's SubscribeRequest proto.","Handle both unary (application/proto) and streaming (application/connect+proto) framing: decode the envelope before extracting the topic string field.","Return 400 with the parser's message (already done) and skip serving that stream, so one bad subscription does not affect other RPCs on the server.","If the sender is a legit LS build, diff its bundled .proto against the parser and fix the field mapping, then add a unit test with a captured request body."],"exampleFix":"// before: single-shot parse, opaque failure\nlet topic = match parse_subscribe_topic_from_connect_body(&parsed.body) {\n    Ok(v) => v,\n    Err(err) => { log_error(&format!(\"... 解析失败: {}\", err)); return Close(text_response(400, \"Bad Request\", &err, ...)); }\n};\n// after: strip connect envelope first and include body context in the log\nlet inner = strip_connect_envelope(&parsed.body, &content_type).unwrap_or(&parsed.body);\nmatch parse_subscribe_topic_from_connect_body(inner) {\n    Ok(v) => v,\n    Err(err) => {\n        log_error(&format!(\"SubscribeToUnifiedStateSyncTopic 解析失败: {} (len={})\", err, inner.len()));\n        return Close(text_response(400, \"Bad Request\", &err, \"text/plain; charset=utf-8\"));\n    }\n}","handlingStrategy":"validation","validationCode":"// Validate framing and presence before topic extraction\nfn subscribable(body: &[u8], content_type: &str) -> bool {\n    let ct = content_type.to_ascii_lowercase();\n    (ct.starts_with(\"application/connect+proto\") || ct.starts_with(\"application/proto\")) && !body.is_empty()\n}","typeGuard":null,"tryCatchPattern":"// Handle per-request parse failure without killing the extension server\nlet topic = match parse_subscribe_topic_from_connect_body(&parsed.body) {\n    Ok(v) => v,\n    Err(err) => {\n        log_error(&format!(\"SubscribeToUnifiedStateSyncTopic 解析失败: {}\", err));\n        return OfficialLsExtensionAction::Close(text_response(400, \"Bad Request\", &err, \"text/plain; charset=utf-8\"));\n    }\n};","preventionTips":["Strip the Connect envelope (length-prefixed frames) before parsing fields for application/connect+proto requests.","Verify the topic string field number against the installed LS's SubscribeRequest proto after every LS upgrade.","Keep serving other RPCs after a bad subscription (isolation), and cap failed-parse logging to avoid log floods.","Record captured request bodies for failing subscriptions to build regression fixtures."],"tags":["protobuf","parsing","grpc","connect-rpc","streaming","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"}