{"record":{"id":"39475db3bcf97253","repo":"gastownhall/beads","slug":"failed-to-scan-peer-for-migration-w","errorCode":null,"errorMessage":"failed to scan peer for migration: %w","messagePattern":"failed to scan peer for migration: %w","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/dolt/credentials.go","lineNumber":166,"sourceCode":"\t\tWHERE password_encrypted IS NOT NULL AND LENGTH(password_encrypted) > 0\n\t`)\n\tif err != nil {\n\t\t// Table may not exist yet (fresh install) — not an error\n\t\treturn nil\n\t}\n\tdefer rows.Close()\n\n\ttype migrationEntry struct {\n\t\tname      string\n\t\tplaintext string\n\t}\n\n\tvar toMigrate []migrationEntry\n\tfor rows.Next() {\n\t\tvar name string\n\t\tvar encrypted []byte\n\t\tif err := rows.Scan(&name, &encrypted); err != nil {\n\t\t\treturn fmt.Errorf(\"failed to scan peer for migration: %w\", err)\n\t\t}\n\n\t\t// Decrypt with old key\n\t\tplaintext, err := decryptWithKey(encrypted, oldKey)\n\t\tif err != nil {\n\t\t\t// Can't decrypt with old key — skip (may already use a different scheme)\n\t\t\tcontinue\n\t\t}\n\t\ttoMigrate = append(toMigrate, migrationEntry{name: name, plaintext: plaintext})\n\t}\n\tif err := rows.Err(); err != nil {\n\t\treturn fmt.Errorf(\"failed to iterate peers for migration: %w\", err)\n\t}\n\n\t// Re-encrypt each password with the new key\n\tfor _, entry := range toMigrate {\n\t\tencrypted, err := encryptWithKey(entry.plaintext, newKey)\n\t\tif err != nil {","sourceCodeStart":148,"sourceCodeEnd":184,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/dolt/credentials.go#L148-L184","documentation":"During credential key migration, migrateCredentialKeys scans each federation_peers row into (name, password_encrypted) with rows.Scan. This error wraps a scan failure, meaning a row's data could not be read into the expected Go types. Because the columns are queried by name from bd's own schema, this almost always indicates schema drift or driver-level data corruption rather than application logic.","triggerScenarios":"rows.Scan(&name, &encrypted) fails while iterating federation_peers during initCredentialKey migration: the row contains NULL in a non-NULL-scanned column (password_encrypted is filtered by the WHERE clause, so this mainly means name is NULL), the column types differ from the expected schema (schema drift/manual DDL), or the connection/driver returns a malformed row mid-stream.","commonSituations":"Manually altered federation_peers schema (columns reordered or types changed by hand-edited SQL); partial/failed Dolt schema migration leaving rows of a different shape; a connection error surfacing as a scan failure on a long-lived server connection.","solutions":["Inspect the federation_peers schema and rows: dolt sql -q \"SELECT name, password_encrypted FROM federation_peers\" to find malformed rows","Restore the expected schema (name NOT NULL, password_encrypted VARBINARY) or fix/re-insert the offending row","Re-run the bd command; if the failure was a transient connection issue, migration retries next open while no key file exists","If a specific peer row is corrupt and unneeded, delete that peer with `bd` peer-removal tooling or a targeted DELETE, then retry"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Check schema integrity before operating on federation_peers\ncols, err := db.Query(\"SHOW COLUMNS FROM federation_peers\")\n// verify expected: name (NOT NULL, string), password_encrypted (VARBINARY)\nif err != nil { return err }","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Never hand-edit federation_peers schema or rows with ad-hoc SQL","Let bd/Dolt migrations manage schema changes exclusively","Restore database backups in full, not partial tables","Use sql.NullString for any nullable column when reading the table directly"],"tags":["database","sql","migration","scan-error"],"backgroundTag":"row-scan-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}