{"record":{"id":"8cb7ff8168d7b952","repo":"gastownhall/beads","slug":"failed-to-iterate-peers-for-migration-w","errorCode":null,"errorMessage":"failed to iterate peers for migration: %w","messagePattern":"failed to iterate peers for migration: %w","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/dolt/credentials.go","lineNumber":178,"sourceCode":"\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 {\n\t\t\treturn fmt.Errorf(\"failed to re-encrypt password for peer %s: %w\", entry.name, err)\n\t\t}\n\t\tif _, err := s.execContext(ctx, `\n\t\t\tUPDATE federation_peers SET password_encrypted = ? WHERE name = ?\n\t\t`, encrypted, entry.name); err != nil {\n\t\t\treturn fmt.Errorf(\"failed to update encrypted password for peer %s: %w\", entry.name, err)\n\t\t}\n\t}\n\n\treturn nil\n}\n","sourceCodeStart":160,"sourceCodeEnd":196,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/dolt/credentials.go#L160-L196","documentation":"After scanning all federation_peers rows, migrateCredentialKeys checks rows.Err() to detect any error that terminated row iteration. This error wraps such an iteration failure — typically a dropped or timed-out database connection mid-scan, or a driver-level read error. It guarantees a broken migration is reported instead of silently re-encrypting only a prefix of the peer list.","triggerScenarios":"rows.Err() returns non-nil after the rows.Next() loop in migrateCredentialKeys: the SQL connection to Dolt dropped or the context was canceled/timed out while streaming rows, or the underlying driver hit an I/O error reading the result set.","commonSituations":"Slow or flaky connection to a remote dolt-sql-server timing out during migration on open; context deadline exceeded because migration ran during a slow startup with many peers; network interruption to a hosted Dolt instance.","solutions":["Increase the context/command timeout or retry — migration re-runs on next open while no key file exists","Verify connectivity to the Dolt server (ping/hosted instance health) and network stability","Check server logs for aborted connections around the failure time","Reduce peer-table contention: ensure no concurrent bd processes hold long transactions on federation_peers"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)\ndefer cancel() // give row iteration room to finish on slow links","typeGuard":null,"tryCatchPattern":"if err := bdSync(); err != nil && strings.Contains(err.Error(), \"failed to iterate peers for migration\") {\n    return retryWithBackoff(bdSync, 3) // migration re-runs safely while no key file exists\n}","preventionTips":["Use stable, low-latency connections to remote dolt-sql-server instances","Set generous timeouts for bd operations over slow networks","Avoid killing bd mid-startup while migration may be in flight"],"tags":["database","sql","network","migration"],"backgroundTag":"rows-iteration-error","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}