{"record":{"id":"5af9037e86896f4a","repo":"k3s-io/k3s","slug":"bootstrap-data-already-found-and-encrypted-with-di","errorCode":null,"errorMessage":"bootstrap data already found and encrypted with different token","messagePattern":"bootstrap data already found and encrypted with different token","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"pkg/cluster/storage.go","lineNumber":341,"sourceCode":"\t\t}\n\t} else {\n\t\tlogrus.Debugf(\"Skipping bootstrap token migration checks: storage client %T is not writable\", storageClient)\n\t}\n\n\t// getting the list of bootstrap again after migrating the empty key\n\tbootstrapList, err = storageClient.List(ctx, \"/bootstrap\", 0)\n\tif err != nil {\n\t\treturn nil, false, err\n\t}\n\tfor _, bootstrapKV := range bootstrapList {\n\t\t// ensure bootstrap is stored in the current token's key\n\t\tlogrus.Debugf(\"checking bootstrap key %s against %s\", string(bootstrapKV.Key), tokenKey)\n\t\tif string(bootstrapKV.Key) == tokenKey {\n\t\t\treturn &bootstrapKV, false, nil\n\t\t}\n\t}\n\n\treturn nil, false, errors.New(\"bootstrap data already found and encrypted with different token\")\n}\n\n// migrateTokens will list all keys that has prefix /bootstrap and will check for key that is\n// hashed with empty string and keys that is hashed with old token format before normalizing\n// then migrate those and resave only with the normalized token\nfunc migrateTokens(ctx context.Context, bootstrapList []mvccpb.KeyValue, storageClient store.ReadWriteCloser, emptyStringKey, tokenKey, token, oldToken string) error {\n\toldTokenKey := storageKey(oldToken)\n\n\tfor _, bootstrapKV := range bootstrapList {\n\t\t// checking for empty string bootstrap key\n\t\tlogrus.Debug(\"Comparing \", string(bootstrapKV.Key), \" to \", oldTokenKey)\n\t\tif string(bootstrapKV.Key) == emptyStringKey {\n\t\t\tlogrus.Warn(\"Bootstrap data encrypted with empty string, deleting and resaving with token\")\n\t\t\tif err := doMigrateToken(ctx, storageClient, bootstrapKV, \"\", emptyStringKey, token, tokenKey); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t} else if string(bootstrapKV.Key) == oldTokenKey && oldTokenKey != tokenKey {\n\t\t\tif emptyStringKey != \"\" {","sourceCodeStart":323,"sourceCodeEnd":359,"githubUrl":"https://github.com/k3s-io/k3s/blob/6ba341e396edc16b8dcae978a7c5e3ac7ee5606e/pkg/cluster/storage.go#L323-L359","documentation":"In getBootstrapKeyFromStorage, bootstrap data exists under '/bootstrap' but no stored key matches tokenKey (the hash of the currently supplied normalized token), and the migration candidates (empty-string key, old-token key) also did not match. The bootstrap is therefore encrypted with a different token than the one presented.","triggerScenarios":"Starting/joining with a token different from the one that encrypted the stored bootstrap; token file and CLI token disagree; partial token rotation where only some servers were updated.","commonSituations":"K3S_TOKEN changed after initial cluster creation; node reinstalled and rejoined with a new agent/server token of the wrong value; copy-paste of only the secret half of the token.","solutions":["Restore and use the original full token (K10<CA hash>::server:<secret>) that the first server generated; it is the passphrase for the stored bootstrap.","If the original token is lost but a healthy server still runs, read the current valid token from that server's /var/lib/rancher/k3s/server/token.","As a last resort, reinitialize the cluster from snapshots with a new token."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Verify the presented token maps to an existing key before starting:\nnorm, _ := util.NormalizeToken(token)\nwant := storageKey(norm) // \"/bootstrap/\" + ShortHash(norm, 12)\nlist, _ := storageClient.List(ctx, \"/bootstrap\", 0)\nfound := false\nfor _, kv := range list { if string(kv.Key) == want { found = true } }\nif !found { return errors.New(\"token does not match stored bootstrap key: use the original cluster token\") }","typeGuard":null,"tryCatchPattern":"if err := startServer(); err != nil {\n\tif strings.Contains(err.Error(), \"encrypted with different token\") {\n\t\t// stop: no code fix; supply the original token or reinit from snapshot\n\t}\n}","preventionTips":["Store the original K10...::server:... token in a secret manager at cluster creation and always source it from there.","Copy the full token including both halves; truncation silently changes the hash.","If you must rotate tokens, follow the documented migration flow, not a flag change."],"tags":["k3s","bootstrap","token","encryption","join"],"backgroundTag":null,"analyzedSha":"6ba341e396edc16b8dcae978a7c5e3ac7ee5606e","analyzedAt":"2026-08-15T16:27:54.286Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}