{"record":{"id":"850406b5a645ff6f","repo":"ipfs/kubo","slug":"querying-orphaned-keystore-data-w","errorCode":null,"errorMessage":"querying orphaned keystore data: %w","messagePattern":"querying orphaned keystore data: %w","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/node/provider.go","lineNumber":552,"sourceCode":"const purgeBatchSize = 1 << 12 // 4096\n\n// purgeOrphanedKeystoreData deletes all keys under /provider/keystore/ from the\n// shared repo datastore. These were written by older Kubo versions that stored\n// provider keystore data inline in the shared datastore. The new code uses\n// separate filesystem datastores under <repo>/{KeystoreDatastorePath}/ instead.\n//\n// The operation is idempotent and safe to interrupt: partial completion is\n// fine because already-deleted keys are no-ops on re-run.\nfunc purgeOrphanedKeystoreData(ctx context.Context, ds datastore.Batching) error {\n\torphanedPrefix := providerDatastoreKey.Child(keystoreDatastoreKey).String()\n\tsyncKey := datastore.NewKey(orphanedPrefix)\n\n\tresults, err := ds.Query(ctx, query.Query{\n\t\tPrefix:   orphanedPrefix,\n\t\tKeysOnly: true,\n\t})\n\tif err != nil {\n\t\treturn fmt.Errorf(\"querying orphaned keystore data: %w\", err)\n\t}\n\tdefer results.Close()\n\n\tvar batch datastore.Batch\n\tvar count, pending int\n\tfor result := range results.Next() {\n\t\tif ctx.Err() != nil {\n\t\t\treturn ctx.Err()\n\t\t}\n\t\tif result.Error != nil {\n\t\t\treturn fmt.Errorf(\"iterating orphaned keystore data: %w\", result.Error)\n\t\t}\n\t\tif batch == nil {\n\t\t\tbatch, err = ds.Batch(ctx)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"creating batch for orphaned keystore cleanup: %w\", err)\n\t\t\t}\n\t\t}","sourceCodeStart":534,"sourceCodeEnd":570,"githubUrl":"https://github.com/ipfs/kubo/blob/329838acdfafae224582930457efe80aa217afc0/core/node/provider.go#L534-L570","documentation":"purgeOrphanedKeystoreData runs a keys-only Query over the datastore with the orphaned-prefix to find keystore entries that no longer belong to any mounted keystore, then batches deletes for them. This error wraps a failure returned by ds.Query itself, meaning the sweep never started — the underlying datastore refused or failed the query. The deferred results.Close() and iteration never happen, so no data was deleted.","triggerScenarios":"Calling purgeOrphanedKeystoreData when ds.Query(ctx, query.Query{Prefix: orphanedPrefix, KeysOnly: true}) errors — e.g. the mounted datastore is closed/unavailable, the underlying flatfs/levelds directory is unreadable or corrupted, or the query was rejected by a datastore that does not support prefix queries in its current state.","commonSituations":"Disk I/O errors or a full disk under the repo directory; keystore datastore directory deleted or permission-changed while the node runs; running the purge against a datastore implementation with query limitations; corrupted keystore database after an unclean shutdown.","solutions":["Inspect the wrapped cause at the end of the chain — it identifies which datastore and why (I/O error, not open, etc.).","Check permissions and disk space/free inodes on the repo directory containing the keystore datastores.","Stop the node, run datastore integrity checks (e.g. flatfs verify, leveldb recovery), and restart.","If the keystore datastore directory was deleted externally, restore it from backup or re-create it via MountKeystoreDatastores.","Retry the purge after fixing the underlying datastore; the error is pre-delete, so it is safe to rerun."],"exampleFix":"// before (purge on possibly-closed datastore)\nresults, err := ds.Query(ctx, query.Query{Prefix: orphanedPrefix, KeysOnly: true})\nif err != nil {\n    return fmt.Errorf(\"querying orphaned keystore data: %w\", err)\n}\n// after (caller-side guard)\nif err := pingDatastore(ctx, ds); err != nil { // health check before purge\n    return fmt.Errorf(\"skipping orphan purge, datastore unhealthy: %w\", err)\n}\nresults, err := ds.Query(ctx, query.Query{Prefix: orphanedPrefix, KeysOnly: true})\nif err != nil {\n    return fmt.Errorf(\"querying orphaned keystore data: %w\", err)\n}","handlingStrategy":"retry","validationCode":"// Before the purge, confirm the datastore answers a trivial query:\nprobe, err := ds.Query(ctx, query.Query{Prefix: orphanedPrefix, KeysOnly: true, Limit: 1})\nif err != nil {\n    return fmt.Errorf(\"datastore unhealthy, aborting purge: %w\", err)\n}\nprobe.Close()","typeGuard":null,"tryCatchPattern":"results, err := ds.Query(ctx, query.Query{Prefix: orphanedPrefix, KeysOnly: true})\nif err != nil {\n    if ctx.Err() != nil {\n        return fmt.Errorf(\"purge canceled: %w\", ctx.Err())\n    }\n    // transient I/O problems justify one bounded retry; nothing was deleted\n    return retryPurge(ctx, ds, 1)\n}","preventionTips":["Monitor free disk space and filesystem health on the repo volume","Run maintenance/purge tasks only while the node owns the repo lock","Back up the keystore datastore directories before purge operations","After unclean shutdowns, run datastore verify/repair before sweeping"],"tags":["go","datastore","query","maintenance"],"backgroundTag":"datastore-query-failed","analyzedSha":"329838acdfafae224582930457efe80aa217afc0","analyzedAt":"2026-09-03T18:30:52.135Z","contentChangedAt":"2026-09-03T18:30:52.135Z","schemaVersion":2},"datasetVersion":"2026-09-11T00:17:11.886Z"}