{"record":{"id":"139c7e297a2965e0","repo":"gastownhall/beads","slug":"failed-to-decrypt-password-w","errorCode":null,"errorMessage":"failed to decrypt password: %w","messagePattern":"failed to decrypt password: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/dolt/credentials.go","lineNumber":356,"sourceCode":"\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to get federation peer: %w\", err)\n\t}\n\n\tif username.Valid {\n\t\tpeer.Username = username.String\n\t}\n\tif lastSync.Valid {\n\t\tpeer.LastSync = &lastSync.Time\n\t}\n\n\t// Decrypt password\n\tif len(encryptedPwd) > 0 {\n\t\tif err := s.ensureCredentialKey(ctx); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"failed to initialize credential key: %w\", err)\n\t\t}\n\t\tpeer.Password, err = s.decryptPassword(encryptedPwd)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"failed to decrypt password: %w\", err)\n\t\t}\n\t}\n\n\treturn &peer, nil\n}\n\n// ListFederationPeers returns all configured federation peers.\nfunc (s *DoltStore) ListFederationPeers(ctx context.Context) ([]*storage.FederationPeer, error) {\n\trows, err := s.queryContext(ctx, `\n\t\tSELECT name, remote_url, username, password_encrypted, sovereignty, last_sync, created_at, updated_at\n\t\tFROM federation_peers ORDER BY name\n\t`)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to list federation peers: %w\", err)\n\t}\n\tdefer rows.Close()\n\n\tvar peers []*storage.FederationPeer","sourceCodeStart":338,"sourceCodeEnd":374,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/dolt/credentials.go#L338-L374","documentation":"GetFederationPeer loads a federation peer row from the federation_peers table and decrypts its stored password with the store's AES-256-GCM credential key. This error is returned when decryptPassword fails, meaning the ciphertext in password_encrypted cannot be authenticated/decrypted with the currently loaded key. The library throws it to prevent silently using a corrupted or wrong-key password for peer sync.","triggerScenarios":"The peer row's password_encrypted was written with a different key than the one now in .beads/.beads-credential-key — e.g. the key file was deleted/regenerated, the workspace was copied without the key file, the DB was restored from backup while the key file is newer, or the ciphertext itself is truncated/corrupt (fails the GCM auth check, 'cipher: message authentication failed').","commonSituations":"Cloning or restoring .beads/dolt data from backup while keeping or regenerating the key file; syncing a workspace between machines without copying .beads-credential-key; manually editing or migrating the federation_peers table; a crash during key migration leaving stale ciphertext.","solutions":["Regenerate the peer's credentials: delete the peer (bd's peer-remove path) and re-add it with AddFederationPeer so the password is re-encrypted with the current key.","Check that .beads/.beads-credential-key exists, is 32 bytes, and matches the one used when the peer was added; restore the original key file from backup if available.","Verify the ciphertext is intact (not truncated by a bad import/restore); re-import the peer row if corrupted.","If the key cannot be recovered, remove all stored peer passwords and re-enter them."],"exampleFix":"// before: peer rows hold ciphertext from an old/lost key\npeer.Password, err = s.decryptPassword(encryptedPwd) // cipher: message authentication failed\n// after: re-add the peer so the password is re-encrypted with the current key\n_ = store.RemoveFederationPeer(ctx, \"peer-name\")\npeer.Password = \"correct-password\"\nerr = store.AddFederationPeer(ctx, peer)","handlingStrategy":"validation","validationCode":"if _, err := os.Stat(filepath.Join(beadsDir, \".beads-credential-key\")); err != nil {\n    return fmt.Errorf(\"credential key missing; re-add peer passwords before syncing: %w\", err)\n}","typeGuard":"func isDecryptFailure(err error) bool {\n    return err != nil && strings.Contains(err.Error(), \"failed to decrypt password\")\n}","tryCatchPattern":"peer, err := store.GetFederationPeer(ctx, name)\nif isDecryptFailure(err) {\n    // re-add peer credentials to re-encrypt with current key\n    _ = store.RemoveFederationPeer(ctx, name)\n    err = store.AddFederationPeer(ctx, &storage.FederationPeer{Name: name, RemoteURL: url, Password: pwd})\n}","preventionTips":["Back up .beads/.beads-credential-key together with the database, never separately","When copying a workspace between machines, copy the whole .beads directory","After restoring from backup, re-add peer passwords instead of assuming old ciphertext still decrypts","Don't regenerate or delete the key file during key-maintenance operations"],"tags":["encryption","credentials","aes-gcm","federation"],"backgroundTag":"credential-decryption-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}