{"record":{"id":"f9c1a8f4f27c5ac2","repo":"kubernetes/kops","slug":"cannot-add-secondary-item-when-no-existing-primary","errorCode":null,"errorMessage":"cannot add secondary item when no existing primary item","messagePattern":"cannot add secondary item when no existing primary item","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"upup/pkg/fi/ca.go","lineNumber":208,"sourceCode":"\t_, err := keyset.AddItem(cert, privateKey, true)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn keyset, nil\n}\n\n// AddItem adds an item to the keyset\nfunc (k *Keyset) AddItem(cert *pki.Certificate, privateKey *pki.PrivateKey, primary bool) (item *KeysetItem, err error) {\n\tif cert == nil {\n\t\treturn item, fmt.Errorf(\"no certificate provided\")\n\t}\n\tif privateKey == nil && primary {\n\t\treturn item, fmt.Errorf(\"private key not provided for primary item\")\n\t}\n\n\tif !primary && k.Primary == nil {\n\t\treturn item, fmt.Errorf(\"cannot add secondary item when no existing primary item\")\n\t}\n\n\thighestId := big.NewInt(0)\n\tfor id := range k.Items {\n\t\titemId, ok := big.NewInt(0).SetString(id, 10)\n\t\tif ok && highestId.Cmp(itemId) < 0 {\n\t\t\thighestId = itemId\n\t\t}\n\t}\n\n\t// Make sure any subsequently created items will have ids that compare higher.\n\t// If setting a primary, make sure its id doesn't compare lower than existing items.\n\tidNumber := pki.BuildPKISerial(time.Now().UnixNano())\n\tif cert.Certificate.SerialNumber.Cmp(idNumber) <= 0 &&\n\t\t(!primary || cert.Certificate.SerialNumber.Cmp(highestId) > 0) {\n\t\tidNumber = cert.Certificate.SerialNumber\n\t}\n","sourceCodeStart":190,"sourceCodeEnd":226,"githubUrl":"https://github.com/kubernetes/kops/blob/4c8573c808a73d578c5eadc86d410646ea0b0d73/upup/pkg/fi/ca.go#L190-L226","documentation":"Keyset items are ordered: the first item added must be the primary. AddItem refuses to attach a secondary item (primary=false) while k.Primary is still nil, otherwise the keyset would have no signing primary. Thrown from upup/pkg/fi/ca.go:208.","triggerScenarios":"Calling AddItem(cert, key, false) on a freshly created/empty Keyset, or iterating items in non-primary-first order (e.g. sorted by id where the newest/secondary id sorts before the primary).","commonSituations":"Rebuilding a keyset from storage where items are iterated in map order (random in Go) and a secondary happens to be processed first; loading old keypairs before the primary during migration or mirror operations.","solutions":["Add the primary item first: AddItem(primaryCert, primaryKey, true), then add secondaries","When rebuilding, locate the item flagged as primary (or the one whose id equals keyset.Spec.PrimaryId) and add it before the rest","Sort/iterate items deterministically so the primary is inserted first"],"exampleFix":"// before\nfor id, item := range items { keyset.AddItem(item.Certificate, item.PrivateKey, id == primaryID) }\n// after\nkeyset.AddItem(items[primaryID].Certificate, items[primaryID].PrivateKey, true)\nfor id, item := range items { if id != primaryID { keyset.AddItem(item.Certificate, item.PrivateKey, false) } }","handlingStrategy":"validation","validationCode":"if keyset.Primary == nil && !isPrimary {\n\treturn fmt.Errorf(\"add the primary item before secondaries\")\n}\nitem, err := keyset.AddItem(cert, privateKey, isPrimary)","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Build keysets in primary-first order, not map-iteration order","Resolve the primary id from keyset.Spec.PrimaryId before inserting items","Add an ordering assertion in tests when rebuilding keysets from storage"],"tags":["go","ca","keyset","ordering"],"backgroundTag":"missing-primary-keyset-item","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"}