{"record":{"id":"6f4d2d54a9de7481","repo":"gastownhall/beads","slug":"ciphertext-too-short","errorCode":null,"errorMessage":"ciphertext too short","messagePattern":"ciphertext too short","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/dolt/credentials.go","lineNumber":226,"sourceCode":"\tif _, err := io.ReadFull(rand.Reader, nonce); err != nil {\n\t\treturn nil, err\n\t}\n\treturn gcm.Seal(nonce, nonce, []byte(plaintext), nil), nil\n}\n\n// decryptWithKey decrypts ciphertext using AES-GCM with the given key.\nfunc decryptWithKey(encrypted []byte, key []byte) (string, error) {\n\tblock, err := aes.NewCipher(key)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tgcm, err := cipher.NewGCM(block)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tnonceSize := gcm.NonceSize()\n\tif len(encrypted) < nonceSize {\n\t\treturn \"\", fmt.Errorf(\"ciphertext too short\")\n\t}\n\tnonce, ciphertext := encrypted[:nonceSize], encrypted[nonceSize:]\n\tplaintext, err := gcm.Open(nil, nonce, ciphertext, nil)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\treturn string(plaintext), nil\n}\n\n// encryptPassword encrypts a password using AES-GCM with the store's credential key.\nfunc (s *DoltStore) encryptPassword(password string) ([]byte, error) {\n\tif password == \"\" {\n\t\treturn nil, nil\n\t}\n\ts.mu.RLock()\n\tkey := s.credentialKey\n\ts.mu.RUnlock()\n\tif key == nil {","sourceCodeStart":208,"sourceCodeEnd":244,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/dolt/credentials.go#L208-L244","documentation":"decryptWithKey splits AES-GCM ciphertext into a nonce prefix and body, using gcm.NonceSize() (12 bytes) as the split point. This error is returned when the ciphertext is shorter than the nonce size, so no valid nonce+body structure exists. It means the stored value cannot possibly be a valid AES-GCM output from this library — it is truncated, empty-ish, or encrypted under some other scheme.","triggerScenarios":"decryptWithKey is called with encrypted bytes of length < 12: a truncated or corrupted password_encrypted value in federation_peers, a value written by a different/older encryption scheme without a nonce prefix, manual tampering with the column, or a test passing deliberately short ciphertext.","commonSituations":"Database rows copied or migrated between workspaces losing bytes; manual SQL edits to password_encrypted; values produced by a pre-AES-GCM legacy format; restoring partial backups; test harnesses feeding malformed input.","solutions":["Re-add the peer's credentials so the password is re-encrypted correctly (the stored value is unrecoverable if truncated)","Check whether the row predates the AES-GCM scheme; if so, re-enter the password via the peer-add flow rather than trying to decrypt it","Restore the affected federation_peers rows from a backup if corruption was accidental","Ensure no tooling or scripts rewrite password_encrypted directly; only use bd's peer management commands"],"exampleFix":null,"handlingStrategy":"type-guard","validationCode":"func looksLikeAESGCM(blob []byte) bool { return len(blob) >= 12 } // nonce(12) + at least one ciphertext/tag byte","typeGuard":"func isDecryptableCiphertext(encrypted []byte) bool {\n    const gcmNonceSize = 12\n    return len(encrypted) >= gcmNonceSize\n}","tryCatchPattern":"pwd, err := decryptPassword(encrypted)\nif err != nil {\n    if strings.Contains(err.Error(), \"ciphertext too short\") {\n        // stored value is corrupt/foreign scheme — prompt to re-enter credentials\n        return reAddPeerCredentials(peerName)\n    }\n    return err\n}","preventionTips":["Never modify password_encrypted values outside bd commands","Back up the full federation_peers table, not partial rows","Re-enter peer passwords after restoring from partial backups","Migrate off pre-AES-GCM formats by re-adding peers once"],"tags":["crypto","aes-gcm","decryption","data-corruption"],"backgroundTag":"ciphertext-too-short","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}