{"record":{"id":"f22e8071ec442d02","repo":"vitessio/vitess","slug":"w-clusterid-is-required","errorCode":null,"errorMessage":"%w: clusterID is required","messagePattern":"%w: clusterID is required","errorType":"validation","errorClass":"ErrInvalidRequest","httpStatus":null,"severity":"error","filePath":"go/vt/vtadmin/api.go","lineNumber":2611,"sourceCode":"\n\tres, err := c.Vtctld.ValidateVersionShard(ctx, &vtctldatapb.ValidateVersionShardRequest{\n\t\tKeyspace: req.Keyspace,\n\t\tShard:    req.Shard,\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn res, nil\n}\n\n// VExplain is part of the vtadminpb.VTAdminServer interface.\nfunc (api *API) VExplain(ctx context.Context, req *vtadminpb.VExplainRequest) (*vtadminpb.VExplainResponse, error) {\n\tspan, ctx := trace.NewSpan(ctx, \"API.VExplain\")\n\tdefer span.Finish()\n\n\tif req.ClusterId == \"\" {\n\t\treturn nil, fmt.Errorf(\"%w: clusterID is required\", errors.ErrInvalidRequest)\n\t}\n\n\tif req.Keyspace == \"\" {\n\t\treturn nil, fmt.Errorf(\"%w: keyspace name is required\", errors.ErrInvalidRequest)\n\t}\n\n\tif req.Sql == \"\" {\n\t\treturn nil, fmt.Errorf(\"%w: SQL query is required\", errors.ErrInvalidRequest)\n\t}\n\n\tc, err := api.getClusterForRequest(req.ClusterId)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif !api.authz.IsAuthorized(ctx, c.ID, rbac.VExplainResource, rbac.GetAction) {\n\t\treturn nil, nil\n\t}","sourceCodeStart":2593,"sourceCodeEnd":2629,"githubUrl":"https://github.com/vitessio/vitess/blob/01a25a7d176f94613b8d59d799f438380a8760e4/go/vt/vtadmin/api.go#L2593-L2629","documentation":"VExplain validates the request before executing; an empty cluster_id fails with ErrInvalidRequest wrapped by this message. It is a pure client-input validation error — no cluster lookup or query occurs.","triggerScenarios":"Calling API.VExplain with a VExplainRequest whose ClusterId field is the empty string.","commonSituations":"Client SDK constructed the request without setting ClusterId; UI passed only keyspace/sql; migrating from VTExplain (which uses req.Cluster) to VExplain and forgetting to rename/populate the field.","solutions":["Set ClusterId to a valid registered cluster ID in the VExplainRequest before sending","Check client code that builds the request for a dropped/renamed field","Validate required fields client-side before issuing the RPC"],"exampleFix":"// before\nreq := &vtadminpb.VExplainRequest{Keyspace: \"ks\", Sql: q}\n// after\nreq := &vtadminpb.VExplainRequest{ClusterId: \"prod-main\", Keyspace: \"ks\", Sql: q}","handlingStrategy":"validation","validationCode":"func validateVExplainRequest(clusterID, keyspace, sql string) error {\n    if clusterID == \"\" {\n        return errors.New(\"clusterID is required\")\n    }\n    if keyspace == \"\" {\n        return errors.New(\"keyspace is required\")\n    }\n    if sql == \"\" {\n        return errors.New(\"sql is required\")\n    }\n    return nil\n}","typeGuard":"func vExplainRequestValid(req *vtadminpb.VExplainRequest) bool {\n    return req != nil && req.ClusterId != \"\" && req.Keyspace != \"\" && req.Sql != \"\"\n}","tryCatchPattern":"if err := validateVExplainRequest(req.ClusterId, req.Keyspace, req.Sql); err != nil {\n    return err\n}\nresp, err := client.VExplain(ctx, req)","preventionTips":["Always construct VExplainRequest with all three fields from a shared helper","Validate required fields at the client boundary before RPC calls","Keep request builders typed/structured instead of hand-built maps"],"tags":["vtadmin","validation","request","vexplain"],"backgroundTag":"missing-required-request-field","analyzedSha":"01a25a7d176f94613b8d59d799f438380a8760e4","analyzedAt":"2026-09-01T17:28:30.605Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}