{"record":{"id":"c2c4e73c668e3bef","repo":"gastownhall/beads","slug":"failed-to-migrate-credential-keys-w","errorCode":null,"errorMessage":"failed to migrate credential keys: %w","messagePattern":"failed to migrate credential keys: %w","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/dolt/credentials.go","lineNumber":93,"sourceCode":"\t\tif oldErr == nil && len(oldKey) == 32 {\n\t\t\t// Write to new location, then remove old file\n\t\t\tif writeErr := os.WriteFile(keyPath, oldKey, 0600); writeErr == nil {\n\t\t\t\t_ = os.Remove(oldKeyPath)\n\t\t\t}\n\t\t\ts.credentialKey = oldKey\n\t\t\treturn nil\n\t\t}\n\t}\n\n\t// Generate new random 32-byte key (AES-256)\n\tkey = make([]byte, 32)\n\tif _, err := io.ReadFull(rand.Reader, key); err != nil {\n\t\treturn fmt.Errorf(\"failed to generate credential encryption key: %w\", err)\n\t}\n\n\t// Migrate existing credentials from old dbPath-derived key to new random key\n\tif err := s.migrateCredentialKeys(ctx, key); err != nil {\n\t\treturn fmt.Errorf(\"failed to migrate credential keys: %w\", err)\n\t}\n\n\t// Write key file with owner-only permissions (0600).\n\t// Ensure the directory exists first — when connecting to an external\n\t// server without having run `bd init`, .beads/ may not exist yet (GH#2641).\n\tif err := os.MkdirAll(s.beadsDir, 0700); err != nil {\n\t\treturn fmt.Errorf(\"failed to create beads directory %s: %w\", s.beadsDir, err)\n\t}\n\tif err := os.WriteFile(keyPath, key, 0600); err != nil {\n\t\treturn fmt.Errorf(\"failed to write credential key file: %w\", err)\n\t}\n\n\ts.credentialKey = key\n\treturn nil\n}\n\n// ensureCredentialKey lazily initializes the credential key when federation\n// operations actually need password encryption or decryption.","sourceCodeStart":75,"sourceCodeEnd":111,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/dolt/credentials.go#L75-L111","documentation":"After generating a new random credential key, initCredentialKey calls migrateCredentialKeys to re-encrypt existing federation peer passwords from the old dbPath-derived (legacy) key to the new random key. This error wraps any failure of that migration — a failed row scan, iteration, re-encryption, or database UPDATE. The store aborts key initialization rather than leaving peer passwords in a mixed encryption scheme.","triggerScenarios":"initCredentialKey runs with a valid database connection and existing rows in federation_peers whose password_encrypted decrypts with the legacy key, and migrateCredentialKeys then fails: rows.Scan error, rows.Err() iteration error, encryptWithKey failure, or the UPDATE federation_peers statement errors (e.g. context canceled, connection dropped, lock timeout).","commonSituations":"Upgrading bd from the legacy key scheme to random keys while the Dolt database is unavailable, locked by another writer, or the context times out mid-migration; stale server connections dropping during the UPDATE loop; concurrent bd processes contending over federation_peers during open.","solutions":["Retry the operation — migration is only attempted when no usable key file exists; once it succeeds the key file is written and migration never runs again","Verify the Dolt database is reachable and accepting writes (run a trivial bd command like `bd ready`)","Check for concurrent bd processes or a dolt-sql-server holding locks on federation_peers and serialize access","Inspect the wrapped inner error (%w chain) for the exact SQL failure and address that root cause"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// Before upgrading, verify the DB is writable\nif err := db.PingContext(ctx); err != nil { return fmt.Errorf(\"dolt unavailable: %w\", err) }","typeGuard":null,"tryCatchPattern":"err := bdCmd()\nif err != nil && strings.Contains(err.Error(), \"failed to migrate credential keys\") {\n    // migration is idempotent while no key file exists — safe to retry\n    return retryWithBackoff(bdCmd, 3)\n}","preventionTips":["Take a backup of .beads and the Dolt DB before upgrading across key-scheme versions","Run one bd process at a time during upgrades to avoid lock contention","Upgrade with a generous context timeout so migration of many peers can finish"],"tags":["database","migration","encryption","dolt"],"backgroundTag":"credential-key-migration-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}