{"record":{"id":"1758f90d31751bd5","repo":"kubernetes/kops","slug":"provider-id-q-is-missing-prefix-q","errorCode":null,"errorMessage":"provider ID %q is missing prefix %q","messagePattern":"provider ID %q is missing prefix %q","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/nodeidentity/do/identify.go","lineNumber":127,"sourceCode":"\n\tbodyBytes, err := io.ReadAll(resp.Body)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"failed to read metadata information %s: %v\", url, err)\n\t}\n\n\treturn string(bodyBytes), nil\n}\n\n// IdentifyNode queries DigitalOcean for the node identity information.\nfunc (i *nodeIdentifier) IdentifyNode(ctx context.Context, node *corev1.Node) (*nodeidentity.Info, error) {\n\tproviderID := node.Spec.ProviderID\n\tif providerID == \"\" {\n\t\treturn nil, errors.New(\"provider ID cannot be empty\")\n\t}\n\n\tconst prefix = \"digitalocean://\"\n\tif !strings.HasPrefix(providerID, prefix) {\n\t\treturn nil, fmt.Errorf(\"provider ID %q is missing prefix %q\", providerID, prefix)\n\t}\n\n\tinstanceID := strings.TrimPrefix(providerID, prefix)\n\tif instanceID == \"\" {\n\t\treturn nil, errors.New(\"provider ID number cannot be empty\")\n\t}\n\n\tif i.cacheEnabled {\n\t\tif obj, exists, err := i.cache.GetByKey(instanceID); err != nil {\n\t\t\tklog.Warningf(\"Nodeidentity info cache lookup failure: %v\", err)\n\t\t} else if exists {\n\t\t\treturn obj.(*nodeidentity.Info), nil\n\t\t}\n\t}\n\n\tdropletID, err := strconv.Atoi(instanceID)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to convert provider ID number %q: %s\", instanceID, err)","sourceCodeStart":109,"sourceCodeEnd":145,"githubUrl":"https://github.com/kubernetes/kops/blob/4c8573c808a73d578c5eadc86d410646ea0b0d73/pkg/nodeidentity/do/identify.go#L109-L145","documentation":"IdentifyNode expects a provider ID in the form 'digitalocean://<id>'. If the supplied providerID string does not start with the 'digitalocean://' scheme, it is rejected with this error because the droplet ID cannot be extracted.","triggerScenarios":"A Node object's spec.providerID was set by a different cloud provider or left in another format (e.g. 'digitalocean:12345', 'do://12345', or a bare numeric ID) and passed to IdentifyNode.","commonSituations":"Clusters migrated between providers; CCM/node controller misconfigured so providerID is populated incorrectly; hand-edited Node specs; older kops/DO CCM versions using a different scheme.","solutions":["Verify the Node's spec.providerID starts with exactly 'digitalocean://'","Correct the providerID source (ensure the DigitalOcean CCM is running and setting providerIDs)","If constructing the ID manually, prefix the numeric droplet ID with 'digitalocean://'","Check for other cloud controllers stamping their own providerID onto the Node"],"exampleFix":"// before\nIdentifyNode(ctx, \"12345678\")\n// after\nIdentifyNode(ctx, \"digitalocean://12345678\")","handlingStrategy":"validation","validationCode":"const prefix = \"digitalocean://\"\nif !strings.HasPrefix(providerID, prefix) {\n    return fmt.Errorf(\"invalid provider ID %q\", providerID)\n}","typeGuard":"func isDOProviderID(id string) bool {\n    return strings.HasPrefix(id, \"digitalocean://\")\n}","tryCatchPattern":"info, err := IdentifyNode(ctx, providerID)\nif err != nil {\n    if strings.Contains(err.Error(), \"missing prefix\") {\n        // reject or fix the providerID before retrying\n    }\n}","preventionTips":["Ensure the DigitalOcean CCM is running so Nodes get correct providerIDs","Validate providerID format before passing it to identity lookups","Never hand-edit Node spec.providerID; let the controller own it","Check for competing cloud controllers stamping other schemes"],"tags":["digitalocean","provider-id","validation","format"],"backgroundTag":"provider-id-missing-prefix","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"}