{"record":{"id":"af4d1dafa70a9aed","repo":"bytebase/bytebase","slug":"expect-string-got-t-for-engine-v","errorCode":null,"errorMessage":"expect string, got %T for engine %v","messagePattern":"expect string, got %T for engine (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"backend/store/instance.go","lineNumber":1082,"sourceCode":"\t\tif variable != \"engine\" {\n\t\t\treturn nil, errors.Errorf(`only \"engine\" support \"engine in [xx]\"/\"!(engine in [xx])\" operator`)\n\t\t}\n\t\tif value == nil {\n\t\t\treturn nil, errors.Errorf(`empty value %v for \"engine\" operator`, value)\n\t\t}\n\t\tlist, ok := value.([]any)\n\t\tif !ok {\n\t\t\treturn nil, errors.Errorf(`expect list, got %T, hint: filter engine in [\"xx\"]`, value)\n\t\t}\n\t\tif len(list) == 0 {\n\t\t\treturn nil, errors.Errorf(`empty value %v for \"engine\" operator`, value)\n\t\t}\n\n\t\tengineList := make([]any, len(list))\n\t\tfor i, raw := range list {\n\t\t\tengine, ok := raw.(string)\n\t\t\tif !ok {\n\t\t\t\treturn nil, errors.Errorf(`expect string, got %T for engine %v`, raw, raw)\n\t\t\t}\n\t\t\tv1Engine, ok := v1pb.Engine_value[engine]\n\t\t\tif !ok {\n\t\t\t\treturn nil, errors.Errorf(`invalid engine filter %q`, engine)\n\t\t\t}\n\t\t\tstoreEngine := convertEngine(v1pb.Engine(v1Engine))\n\t\t\tengineList[i] = storeEngine\n\t\t}\n\t\treturn qb.Q().Space(\"instance.metadata->>'engine' = ANY(?)\", engineList), nil\n\t}\n\n\tgetFilter = func(expr celast.Expr) (*qb.Query, error) {\n\t\tq := qb.Q()\n\t\tswitch expr.Kind() {\n\t\tcase celast.CallKind:\n\t\t\tfunctionName := expr.AsCall().FunctionName()\n\t\t\tswitch functionName {\n\t\t\tcase celoperators.LogicalOr:","sourceCodeStart":1064,"sourceCodeEnd":1100,"githubUrl":"https://github.com/bytebase/bytebase/blob/1870550677fe08f0d2a78c07acd27541464eb945/backend/store/instance.go#L1064-L1100","documentation":"In parseToEngineSQL (backend/store/instance.go), each element of an `engine in [...]` list must be a string literal. This error is thrown when a list element is a non-string Go type (number, bool, etc.), aborting before SQL generation.","triggerScenarios":"Filters like `engine in [1, 2]` or `engine in [true]` — numeric or boolean literals inside the engine list.","commonSituations":"Programmatic filter builders substituting enum ordinal values instead of engine name strings, JSON payloads where engines were serialized as numbers.","solutions":["Use the engine name string for each element, e.g. `engine in [\"POSTGRES\"]`.","Check v1pb.Engine enum names (Engine_value keys) and use exactly those strings.","Fix the client filter builder to stringify engine values."],"exampleFix":"// before\nfilter = 'engine in [1, 2]'\n// after\nfilter = 'engine in [\"POSTGRES\", \"MYSQL\"]'","handlingStrategy":"type-guard","validationCode":"engineList.forEach(e => { if (typeof e !== 'string') throw new TypeError('engine list items must be strings: ' + e); });","typeGuard":"const isStringEngineList = (v) => Array.isArray(v) && v.every(x => typeof x === 'string');","tryCatchPattern":"try {\n  return await listInstances({ filter });\n} catch (e) {\n  if (String(e).includes('expect string')) {\n    filter = buildEngineFilter(engineList.map(String));\n    return retry(listInstances, { filter });\n  }\n  throw e;\n}","preventionTips":["Never pass enum ordinals; always use engine name strings.","Stringify values when interpolating them into filter templates."],"tags":["cel","filter","type-mismatch","engine"],"backgroundTag":"type-mismatch","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"}