{"record":{"id":"54761e6eaf53739b","repo":"kubernetes/kops","slug":"expected-an-aws-cloud-got-t","errorCode":null,"errorMessage":"expected an AWS cloud, got %T","messagePattern":"expected an AWS cloud, got %T","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"upup/pkg/fi/cloudup/template_functions_karpenter.go","lineNumber":388,"sourceCode":"\t}\n\n\treturn []karpenterBlockDeviceMapping{\n\t\t{\n\t\t\tDeviceName: rootDeviceName,\n\t\t\tEBS:        ebs,\n\t\t\tRootVolume: new(true),\n\t\t},\n\t}, nil\n}\n\n// karpenterRootDeviceName resolves the root device name of the InstanceGroup image, so\n// that the generated block device mapping overrides the image's root volume rather than\n// attaching an additional one. The name varies between images (/dev/xvda, /dev/sda1),\n// so it has to come from the image itself.\nfunc (tf *TemplateFunctions) karpenterRootDeviceName(image string) (string, error) {\n\tcloud, ok := tf.cloud.(awsup.AWSCloud)\n\tif !ok {\n\t\treturn \"\", fmt.Errorf(\"expected an AWS cloud, got %T\", tf.cloud)\n\t}\n\tresolved, err := cloud.ResolveImage(image)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"unable to resolve image %q: %w\", image, err)\n\t}\n\tif resolved == nil {\n\t\treturn \"\", fmt.Errorf(\"unable to resolve image %q: not found\", image)\n\t}\n\trootDeviceName := fi.ValueOf(resolved.RootDeviceName)\n\tif rootDeviceName == \"\" {\n\t\treturn \"\", fmt.Errorf(\"image %q has no root device name\", image)\n\t}\n\treturn rootDeviceName, nil\n}\n\nfunc (tf *TemplateFunctions) buildKarpenterNodePool(ig *kops.InstanceGroup) (*karpenterNodePool, error) {\n\tlabels, err := nodelabels.BuildNodeLabels(tf.Cluster, ig)\n\tif err != nil {","sourceCodeStart":370,"sourceCodeEnd":406,"githubUrl":"https://github.com/kubernetes/kops/blob/4c8573c808a73d578c5eadc86d410646ea0b0d73/upup/pkg/fi/cloudup/template_functions_karpenter.go#L370-L406","documentation":"karpenterRootDeviceName resolves an AMI's root device name (/dev/xvda, /dev/sda1, etc.) so Karpenter EC2NodeClass block device mappings override the image's root volume. The template function's cloud field must be an awsup.AWSCloud to call ResolveImage; this error is thrown when tf.cloud holds any other cloud implementation or nil, i.e. Karpenter EC2NodeClass generation was attempted on a non-AWS cluster.","triggerScenarios":"Running `kops update cluster` with an InstanceGroup using the Karpenter provider on a cluster whose cloud is not AWS (GCE, DO, Hetzner, OpenStack, Azure), or tf.cloud being nil/uninitialized when buildKarpenterEC2NodeClass invokes karpenterRootDeviceName.","commonSituations":"Copy-pasting a cluster config with karpenterInstanceGroups from an AWS cluster into a non-AWS cluster; forgetting to set cloudProvider/aws on the cluster spec; unit tests constructing TemplateFunctions without an AWSCloud (as in TestKarpenterRootDeviceNameWithoutAWSCloud).","solutions":["Ensure the cluster's cloudProvider is aws before using providerRole/karpenter instance groups","Check the cluster spec: cloudConfig.aws should be configured and `kops get cluster -oyaml` should show cloudProvider aws","Move the Karpenter instance group to an AWS-only cluster, or use the cloud's native autoscaling instead","If writing tests, inject a mock or real awsup.AWSCloud into tf.cloud"],"exampleFix":"// before (non-AWS cluster spec)\ncloudProvider: gce\ninstanceGroups: [{ name: karpenter-ig, karpenter: {...} }]\n// after\ncloudProvider: aws\n# or remove the karpenter instance group on non-AWS clusters","handlingStrategy":"type-guard","validationCode":"// before using karpenter template functions\nif _, ok := tf.cloud.(awsup.AWSCloud); !ok {\n\treturn fmt.Errorf(\"karpenter requires AWS; cluster cloud is %T\", tf.cloud)\n}","typeGuard":"func isAWSCloud(c fi.Cloud) bool {\n\t_, ok := c.(awsup.AWSCloud)\n\treturn ok\n}","tryCatchPattern":"// Go: check the error returned by the template function\nrootDev, err := tf.karpenterRootDeviceName(image)\nif err != nil {\n\tif strings.Contains(err.Error(), \"expected an AWS cloud\") {\n\t\treturn fmt.Errorf(\"karpenter is AWS-only: %w\", err)\n\t}\n\treturn err\n}","preventionTips":["Only enable karpenter instance groups on clusters with cloudProvider: aws","Gate Karpenter template rendering behind a cluster cloud check early in updateCluster","Add a unit test asserting non-AWS clusters never reach buildKarpenterEC2NodeClass"],"tags":["aws","karpenter","type-assertion","cloud-provider"],"backgroundTag":"expected-aws-cloud","analyzedSha":"4c8573c808a73d578c5eadc86d410646ea0b0d73","analyzedAt":"2026-09-05T04:13:19.212Z","contentChangedAt":"2026-09-05T04:13:19.212Z","schemaVersion":2},"datasetVersion":"2026-09-12T12:17:11.808Z"}