{"record":{"id":"a9eeaf9acdf521bf","repo":"rancher/rancher","slug":"either-region-or-zone-is-required","errorCode":null,"errorMessage":"either region or zone is required","messagePattern":"either region or zone is required","errorType":"validation","errorClass":null,"httpStatus":400,"severity":"error","filePath":"pkg/api/norman/customization/gke/listers.go","lineNumber":144,"sourceCode":"\t}\n\n\tclient, err := getIamServiceClient(ctx, cap.Credentials)\n\tif err != nil {\n\t\treturn nil, http.StatusInternalServerError, err\n\t}\n\n\tname := \"projects/\" + cap.ProjectID\n\tresult, err := client.Projects.ServiceAccounts.List(name).Do()\n\tif err != nil {\n\t\treturn nil, http.StatusInternalServerError, err\n\t}\n\n\treturn encodeOutput(result)\n}\n\nfunc listVersions(ctx context.Context, cap *Capabilities) ([]byte, int, error) {\n\tif cap.Region == \"\" && cap.Zone == \"\" {\n\t\treturn nil, http.StatusBadRequest, fmt.Errorf(\"either region or zone is required\")\n\t}\n\tif cap.Region != \"\" && cap.Zone != \"\" {\n\t\treturn nil, http.StatusBadRequest, fmt.Errorf(\"only one of region or zone can be provided\")\n\t}\n\tif cap.ProjectID == \"\" {\n\t\treturn nil, http.StatusBadRequest, fmt.Errorf(\"projectId is required\")\n\t}\n\n\tclient, err := getContainerServiceClient(ctx, cap.Credentials)\n\tif err != nil {\n\t\treturn nil, http.StatusInternalServerError, err\n\t}\n\n\tvar location string\n\tif cap.Region != \"\" {\n\t\tlocation = cap.Region\n\t} else {\n\t\tlocation = cap.Zone","sourceCodeStart":126,"sourceCodeEnd":162,"githubUrl":"https://github.com/rancher/rancher/blob/932558d4e68565aff2d2f36e89ec4a391b06e7c5/pkg/api/norman/customization/gke/listers.go#L126-L162","documentation":"listVersions validates location first: exactly one of Region or Zone must be set, because GKE server configs are fetched per location (regions.serverConfigs for regional clusters, zones.serverConfigs for zonal). When both are empty, HTTP 400 'either region or zone is required' is returned before the projectId check and before any GCP call.","triggerScenarios":"Calling resourceType=gkeVersions with neither ?region= nor ?zone=; the location form section untouched (Kubernetes version dropdown fetched too early); location reset after a credential or project change.","commonSituations":"UI fetching versions before the user picks regional vs zonal; scripts that only send credential and projectId; location state lost on component re-mount.","solutions":["Add exactly one location param: ...&resourceType=gkeVersions&projectId=my-project&zone=us-central1-a (or region=us-central1)","In the UI, disable the version dropdown until a location type and value are chosen","Remember projectId is still required separately after fixing location"],"exampleFix":"// before\nGET /v3/gke/endpoints?resourceType=gkeVersions&cloudCredentialId=ns:n&projectId=my-project\n// after\nGET /v3/gke/endpoints?resourceType=gkeVersions&cloudCredentialId=ns:n&projectId=my-project&zone=us-central1-a","handlingStrategy":"validation","validationCode":"function locationParam(region, zone) {\n  if (!region && !zone) throw new Error('gkeVersions requires exactly one of region or zone');\n  return zone ? { zone } : { region };\n}","typeGuard":"const hasExactlyOneLocation = (cap) => Boolean(cap?.region) !== Boolean(cap?.zone);","tryCatchPattern":null,"preventionTips":["Make the version fetch wait on a completed location selection (regional vs zonal toggle + value)","Model location as a single discriminated field in client state, never two independent strings","Note projectId is still required after location passes — validate both together"],"tags":["gke","validation","region","zone","http-400"],"backgroundTag":null,"analyzedSha":"932558d4e68565aff2d2f36e89ec4a391b06e7c5","analyzedAt":"2026-08-16T04:37:02.125Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}