{"record":{"id":"dbaa42222fda2bc1","repo":"k3s-io/k3s","slug":"invalid-cipher-text-not-delimited","errorCode":null,"errorMessage":"invalid cipher text, not : delimited","messagePattern":"invalid cipher text, not : delimited","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/cluster/encrypt.go","lineNumber":57,"sourceCode":"\t\treturn nil, err\n\t}\n\n\tnonce := make([]byte, gcm.NonceSize())\n\t_, err = io.ReadFull(rand.Reader, nonce)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tsealed := gcm.Seal(nonce, nonce, plaintext, nil)\n\treturn []byte(salt + \":\" + base64.StdEncoding.EncodeToString(sealed)), nil\n}\n\n// decrypt attempts to decrypt the byte slice using the supplied passphrase.\n// The input byte slice should be the ciphertext output from the encrypt function.\nfunc decrypt(passphrase string, ciphertext []byte) ([]byte, error) {\n\tparts := strings.SplitN(string(ciphertext), \":\", 2)\n\tif len(parts) != 2 {\n\t\treturn nil, errors.New(\"invalid cipher text, not : delimited\")\n\t}\n\n\tclearKey := pbkdf2.Key([]byte(passphrase), []byte(parts[0]), 4096, 32, sha1.New)\n\tkey, err := aes.NewCipher(clearKey)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tgcm, err := cipher.NewGCM(key)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tdata, err := base64.StdEncoding.DecodeString(parts[1])\n\tif err != nil {\n\t\treturn nil, err\n\t}\n","sourceCodeStart":39,"sourceCodeEnd":75,"githubUrl":"https://github.com/k3s-io/k3s/blob/6ba341e396edc16b8dcae978a7c5e3ac7ee5606e/pkg/cluster/encrypt.go#L39-L75","documentation":"decrypt() requires ciphertext in the exact form produced by encrypt(): '<salt>:<base64 AES-GCM sealed data>'. SplitN on ':' yields fewer than 2 parts when the value contains no colon, so the stored bytes are not ciphertext from this scheme and cannot be decrypted with any passphrase.","triggerScenarios":"The '/bootstrap' key value was corrupted, truncated, base64-decoded, or overwritten with plaintext/manual bytes; a datastore restore wrote the wrong bytes; code passed an arbitrary buffer (e.g., an unencrypted value from a foreign tool) into decrypt.","commonSituations":"Datastore restored from a partially corrupt backup; someone hand-edited the etcd key; version mix where an older format wrote values without the salt prefix.","solutions":["Confirm the value stored under the token-derived bootstrap key still contains a colon: etcdctl get <bootstrap-key> and inspect; if not, it is corrupt.","Restore the datastore from a known-good etcd snapshot taken while the cluster was healthy.","If no good snapshot exists, back up and delete the db directory and reinitialize the cluster (--cluster-init), then rejoin peers.","Verify you are decrypting with the matching token only after the format is confirmed; a wrong token fails later in gcm.Open, not here."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Mirror the format check decrypt() performs, before calling it:\nfunc isEncryptedBootstrap(v []byte) bool {\n\tparts := strings.SplitN(string(v), \":\", 2)\n\treturn len(parts) == 2 && parts[0] != \"\" && parts[1] != \"\"\n}\nif !isEncryptedBootstrap(kv.Value) { /* corrupt: restore from snapshot */ }","typeGuard":null,"tryCatchPattern":"if _, err := decrypt(token, kv.Value); err != nil {\n\tif strings.Contains(err.Error(), \"not : delimited\") {\n\t\t// value corrupt/foreign: restore datastore from snapshot, do not retry with other tokens\n\t} else {\n\t\t// wrong token (gcm.Open auth failure) or transient error\n\t}\n}","preventionTips":["Never hand-edit /bootstrap keys; the value format is salt:base64(gcm) and irreversible if damaged.","Take etcd snapshots before any datastore maintenance so a format-corrupt value is recoverable.","Distinguish the two failure modes: no colon = corrupt data; cipher auth failure = wrong token."],"tags":["k3s","encryption","aes-gcm","bootstrap","corruption"],"backgroundTag":null,"analyzedSha":"6ba341e396edc16b8dcae978a7c5e3ac7ee5606e","analyzedAt":"2026-08-15T16:27:54.286Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}