{"record":{"id":"7ec0c8d186012a34","repo":"bytebase/bytebase","slug":"expected-any-for-row-but-got-t","errorCode":null,"errorMessage":"expected []any for row but got %T","messagePattern":"expected \\[\\]any for row but got %T","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"backend/plugin/advisor/oceanbase/common.go","lineNumber":37,"sourceCode":"\tif len(res) < 3 {\n\t\treturn 0, errors.Errorf(\"expected at least 3 elements but got %d\", len(res))\n\t}\n\n\trowList, ok := res[2].([]any)\n\tif !ok {\n\t\treturn 0, errors.Errorf(\"expected []any for row data but got %T\", res[2])\n\t}\n\tif len(rowList) == 0 {\n\t\treturn 0, errors.Errorf(\"no data returned from EXPLAIN\")\n\t}\n\n\t// OceanBase might return JSON data split across multiple elements\n\t// We need to concatenate them to form a valid JSON string\n\tvar jsonStr string\n\tfor _, rowAny := range rowList {\n\t\trow, ok := rowAny.([]any)\n\t\tif !ok {\n\t\t\treturn 0, errors.Errorf(\"expected []any for row but got %T\", rowAny)\n\t\t}\n\t\tfor _, cellAny := range row {\n\t\t\tif cell, ok := cellAny.(string); ok {\n\t\t\t\tjsonStr += cell\n\t\t\t}\n\t\t}\n\t}\n\n\tif jsonStr == \"\" {\n\t\treturn 0, errors.Errorf(\"no JSON data found in EXPLAIN result\")\n\t}\n\n\tvar explainData ExplainJSON\n\tif err := json.Unmarshal([]byte(jsonStr), &explainData); err != nil {\n\t\treturn 0, errors.Errorf(\"failed to parse JSON: %v\", err)\n\t}\n\n\t// Return the estimated rows from the JSON response","sourceCodeStart":19,"sourceCodeEnd":55,"githubUrl":"https://github.com/bytebase/bytebase/blob/1870550677fe08f0d2a78c07acd27541464eb945/backend/plugin/advisor/oceanbase/common.go#L19-L55","documentation":"OceanBase advisor getEstimatedRowsFromJSON: an element of the row-data list is not itself a []any row. The JSON explain output was split across elements in an unexpected shape, breaking the concatenation that rebuilds the JSON string.","triggerScenarios":"A row inside res[2] decoded as []string, map, or nil — typically a custom driver, proxy, or test double that returns rows in a different concrete type than the official OceanBase driver.","commonSituations":"Third-party drivers decoding rows as typed slices; intermediary API layers reshaping results; mocks returning []string rows.","solutions":["Check the %T in the error and fix the driver/serialization layer to emit []any rows.","Convert other slice types (e.g. []string) to []any in the caller before extraction.","Use the official OceanBase Go driver or align the wrapper's output.","Pin a driver version known to produce []any rows for EXPLAIN results."],"exampleFix":"// before\nrow, ok := rowAny.([]any)\nif !ok {\n\treturn 0, errors.Errorf(\"expected []any for row but got %T\", rowAny)\n}\n// after\nvar row []any\nswitch r := rowAny.(type) {\ncase []any:\n\trow = r\ncase []string:\n\trow = make([]any, len(r))\n\tfor i, s := range r {\n\t\trow[i] = s\n\t}\ndefault:\n\treturn 0, errors.Errorf(\"expected []any for row but got %T\", rowAny)\n}","handlingStrategy":"type-guard","validationCode":"func allRowsTyped(rows []any) bool {\n\tfor _, r := range rows {\n\t\tif _, ok := r.([]any); !ok {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}","typeGuard":"func asRow(v any) ([]any, bool) {\n\trow, ok := v.([]any)\n\treturn row, ok\n}","tryCatchPattern":null,"preventionTips":["Convert typed row slices (e.g. []string) to []any at the driver boundary.","Test the parser with the exact concrete types your driver returns.","Avoid SDK/proxy wrappers that change row representation."],"tags":["oceanbase","advisor","explain","type-assertion"],"backgroundTag":"unexpected-api-response-shape","analyzedSha":"1870550677fe08f0d2a78c07acd27541464eb945","analyzedAt":"2026-09-06T21:16:13.665Z","contentChangedAt":"2026-09-06T21:16:13.665Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}