bytebase/bytebase · error

expected []any for row but got %T

Error message

expected []any for row but got %T

What it means

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.

Source

Thrown at backend/plugin/advisor/oceanbase/common.go:37

	if len(res) < 3 {
		return 0, errors.Errorf("expected at least 3 elements but got %d", len(res))
	}

	rowList, ok := res[2].([]any)
	if !ok {
		return 0, errors.Errorf("expected []any for row data but got %T", res[2])
	}
	if len(rowList) == 0 {
		return 0, errors.Errorf("no data returned from EXPLAIN")
	}

	// OceanBase might return JSON data split across multiple elements
	// We need to concatenate them to form a valid JSON string
	var jsonStr string
	for _, rowAny := range rowList {
		row, ok := rowAny.([]any)
		if !ok {
			return 0, errors.Errorf("expected []any for row but got %T", rowAny)
		}
		for _, cellAny := range row {
			if cell, ok := cellAny.(string); ok {
				jsonStr += cell
			}
		}
	}

	if jsonStr == "" {
		return 0, errors.Errorf("no JSON data found in EXPLAIN result")
	}

	var explainData ExplainJSON
	if err := json.Unmarshal([]byte(jsonStr), &explainData); err != nil {
		return 0, errors.Errorf("failed to parse JSON: %v", err)
	}

	// Return the estimated rows from the JSON response

View on GitHub (pinned to 1870550677)

Solutions

  1. Check the %T in the error and fix the driver/serialization layer to emit []any rows.
  2. Convert other slice types (e.g. []string) to []any in the caller before extraction.
  3. Use the official OceanBase Go driver or align the wrapper's output.
  4. Pin a driver version known to produce []any rows for EXPLAIN results.

Example fix

// before
row, ok := rowAny.([]any)
if !ok {
	return 0, errors.Errorf("expected []any for row but got %T", rowAny)
}
// after
var row []any
switch r := rowAny.(type) {
case []any:
	row = r
case []string:
	row = make([]any, len(r))
	for i, s := range r {
		row[i] = s
	}
default:
	return 0, errors.Errorf("expected []any for row but got %T", rowAny)
}
Defensive patterns

Strategy: type-guard

Validate before calling

func allRowsTyped(rows []any) bool {
	for _, r := range rows {
		if _, ok := r.([]any); !ok {
			return false
		}
	}
	return true
}

Type guard

func asRow(v any) ([]any, bool) {
	row, ok := v.([]any)
	return row, ok
}

Prevention

When it happens

Trigger: 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.

Common situations: Third-party drivers decoding rows as typed slices; intermediary API layers reshaping results; mocks returning []string rows.

Understand the failure class

Background: "invalid response format", "malformed payload", "missing data field": when an API returns 200 but the response shape is wrong — this error's family across 23 libraries.

Related errors


AI-assisted analysis of bytebase/bytebase@1870550677 (2026-09-06). Data as JSON: /api/errors/7ec0c8d186012a34. Report an issue: GitHub.