{"record":{"id":"47128da4516af6b5","repo":"t8y2/dbx","slug":"decode-topic-stats-w","errorCode":null,"errorMessage":"decode topic stats: %w","messagePattern":"decode topic stats: %w","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"agents/drivers/rocketmq/topics.go","lineNumber":312,"sourceCode":"\t\t}\n\t\tsuccessCount++\n\t\tfor key, offset := range partial {\n\t\t\tmerged[key] = offset\n\t\t}\n\t}\n\tif successCount != len(addresses) {\n\t\treturn nil, fmt.Errorf(\"query topic stats for %s on all masters: %w\", topic, lastErr)\n\t}\n\tif len(merged) == 0 {\n\t\treturn nil, fmt.Errorf(\"topic stats not found: %s\", topic)\n\t}\n\treturn merged, nil\n}\n\nfunc decodeTopicStats(body []byte) (map[string]*admin.TopicOffset, error) {\n\tvar stats admin.TopicStatsTable\n\tif err := json.Unmarshal(repairRocketMQJSON(body), &stats); err != nil {\n\t\treturn nil, fmt.Errorf(\"decode topic stats: %w\", err)\n\t}\n\tif stats.OffsetTable == nil {\n\t\tstats.OffsetTable = make(map[string]*admin.TopicOffset)\n\t}\n\treturn stats.OffsetTable, nil\n}\n\nfunc masterAddressesFromRoute(route *admin.TopicRouteData) []string {\n\taddresses := make(map[string]struct{})\n\tfor _, broker := range route.BrokerDatas {\n\t\tif address := broker.BrokerAddrs[\"0\"]; address != \"\" {\n\t\t\taddresses[address] = struct{}{}\n\t\t}\n\t}\n\treturn sortedKeys(addresses)\n}\n\nfunc (a *rocketMQAgent) getTopicConfig(params map[string]any) (any, error) {","sourceCodeStart":294,"sourceCodeEnd":330,"githubUrl":"https://github.com/t8y2/dbx/blob/c0390bff16418b651f4728520d99adf8ce48829a/agents/drivers/rocketmq/topics.go#L294-L330","documentation":"decodeTopicStats wraps any JSON unmarshal failure of the broker's GetTopicStatsInfo response body. The library first runs repairRocketMQJSON (RocketMQ brokers sometimes emit non-standard JSON with unquoted integer keys), so this error means even after repair the body could not be parsed into admin.TopicStatsTable.","triggerScenarios":"A master broker returns a malformed or non-JSON body for GetTopicStatsInfo (examineTopicStats calls decodeTopicStats on each response); also produced directly by TestDecodeTopicStatsRepairsObjectKeys when feeding corrupt payloads.","commonSituations":"Older or patched RocketMQ broker versions emitting legacy JSON with bare numeric keys that repairRocketMQJSON cannot fix; a proxy/LB returning an HTML error page instead of the broker response; truncated responses on flaky networks; wrong remoting protocol version.","solutions":["Capture and inspect the raw response body to see what the broker actually returned","Upgrade the RocketMQ broker or driver so the stats response uses standard JSON that repairRocketMQJSON handles","Check for proxies/LBs or TLS endpoints mangling the body — connect directly to the broker master address","Retry the request; if it is transient (truncated body), the per-master loop in examineTopicStats already records it as lastErr"],"exampleFix":"// before\npartial, err := decodeTopicStats(response.Body)\nif err != nil { return nil, err }\n// after\nif !json.Valid(response.Body) {\n    return nil, fmt.Errorf(\"broker returned non-JSON body (%d bytes): %q\", len(response.Body), response.Body[:min(64, len(response.Body))])\n}\npartial, err := decodeTopicStats(response.Body)\nif err != nil { return nil, err }","handlingStrategy":"retry","validationCode":"if len(body) == 0 || body[0] != '{' {\n    return fmt.Errorf(\"unexpected stats response, not JSON: %q\", body)\n}","typeGuard":"func isDecodeTopicStatsErr(err error) bool {\n    return err != nil && strings.Contains(err.Error(), \"decode topic stats\")\n}","tryCatchPattern":"partial, err := decodeTopicStats(body)\nif err != nil {\n    if isDecodeTopicStatsErr(err) {\n        log.Printf(\"bad stats body from broker, retrying: %v\", err)\n        return retryFetch()\n    }\n    return err\n}","preventionTips":["Keep broker and driver versions aligned so response JSON is standard","Bypass proxies/LBs for remoting calls to brokers","Retry transient decode failures — the per-master loop tolerates one bad broker"],"tags":["rocketmq","json","decode"],"backgroundTag":"json-parse-error","analyzedSha":"c0390bff16418b651f4728520d99adf8ce48829a","analyzedAt":"2026-09-05T23:05:10.900Z","contentChangedAt":"2026-09-05T23:05:10.900Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}