{"record":{"id":"a98fbab1d4fc4d48","repo":"ipfs/kubo","slug":"unexpected-keystore-suffix-q-expected-0-or-1","errorCode":null,"errorMessage":"unexpected keystore suffix %q, expected \"0\" or \"1\"","messagePattern":"unexpected keystore suffix %q, expected \"0\" or \"1\"","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/node/provider.go","lineNumber":89,"sourceCode":"\tkeystoreDatastoreKey = datastore.NewKey(\"keystore\")\n)\n\n// providerLog is the go-log subsystem used for provide/reprovide-related\n// messages emitted from kubo's own orchestration code. It shares the\n// \"provider\" subsystem name with boxo's provider package so users can set\n// GOLOG_LOG_LEVEL=provider=<level> to control both layers at once. See\n// docs/debug-guide.md for the full list of provide-related subsystems.\nvar providerLog = log.Logger(\"provider\")\n\nvar errAcceleratedDHTNotReady = errors.New(\"AcceleratedDHTClient: routing table not ready\")\n\n// validateKeystoreSuffix rejects any suffix other than \"0\" or \"1\".\n// The upstream library uses these two values as alternating namespace\n// identifiers. Validating here prevents accidental deletion of unrelated\n// directories via os.RemoveAll if the upstream ever changes its scheme.\nfunc validateKeystoreSuffix(suffix string) error {\n\tif suffix != \"0\" && suffix != \"1\" {\n\t\treturn fmt.Errorf(\"unexpected keystore suffix %q, expected \\\"0\\\" or \\\"1\\\"\", suffix)\n\t}\n\treturn nil\n}\n\n// Interval between reprovide queue monitoring checks for slow reprovide alerts.\n// Used when Provide.DHT.SweepEnabled=true\nconst reprovideAlertPollInterval = 15 * time.Minute\n\n// Number of consecutive polling intervals with sustained queue growth before\n// triggering a slow reprovide alert (3 intervals = 45 minutes).\n// Used when Provide.DHT.SweepEnabled=true\nconst consecutiveAlertsThreshold = 3\n\n// DHTProvider is an interface for providing keys to a DHT swarm. It holds a\n// state of keys to be advertised, and is responsible for periodically\n// publishing provider records for these keys to the DHT swarm before the\n// records expire.\ntype DHTProvider interface {","sourceCodeStart":71,"sourceCodeEnd":107,"githubUrl":"https://github.com/ipfs/kubo/blob/329838acdfafae224582930457efe80aa217afc0/core/node/provider.go#L71-L107","documentation":"This error comes from validateKeystoreSuffix in core/node/provider.go, which guards the alternating-keystore namespace scheme used by the reprovide/provider system. The upstream keystore layout alternates between two suffix directories, \"0\" and \"1\"; any other suffix means the code would be operating on a directory it does not understand. The check exists specifically so a future upstream scheme change cannot cause os.RemoveAll to delete unrelated directories. If you see it, the computed keystore suffix fell outside the two known values.","triggerScenarios":"Calling into the keystore swap/cleanup path when the suffix variable passed to validateKeystoreSuffix is anything other than the literal \"0\" or \"1\" — typically a bug in the caller computing the suffix, or an upstream library version that changed its naming scheme.","commonSituations":"Running a kubo build paired with a newer/older version of the upstream provider library that altered its keystore directory naming; local manual edits or scripts that created extra keystore directories; a regression in the alternating-suffix calculation.","solutions":["Check the version of the upstream provider library in go.mod against what kubo expects; align it (go get <lib>@<pin> + make mod_tidy).","Inspect the keystore directories under the repo datastore path; remove or rename any directory whose suffix is not \"0\" or \"1\" (after backing up).","Search the code path computing the suffix for a regression and fix the calculation so only \"0\"/\"1\" are produced.","If neither suffix applies, this is a hard stop by design — do not bypass the guard; report the upstream scheme change to kubo/boxo."],"exampleFix":"// before\nsuffix := fmt.Sprint(counter) // may yield \"2\", \"3\", ...\nif err := validateKeystoreSuffix(suffix); err != nil { ... }\n// after\nsuffix := \"0\"\nif counter%2 == 1 {\n    suffix = \"1\"\n}\nif err := validateKeystoreSuffix(suffix); err != nil { ... }","handlingStrategy":"validation","validationCode":"func safeKeystoreSuffix(n int) (string, error) {\n    s := strconv.Itoa(n % 2)\n    if s != \"0\" && s != \"1\" {\n        return \"\", fmt.Errorf(\"computed suffix %q outside {0,1}\", s)\n    }\n    return s, nil\n}","typeGuard":"func isValidKeystoreSuffix(s string) bool { return s == \"0\" || s == \"1\" }","tryCatchPattern":null,"preventionTips":["Always derive the suffix with modulo-2 of a counter, never from free-form input","Never construct keystore paths by concatenating unvalidated strings","Pin the upstream provider library version and review its changelog for naming-scheme changes","Treat any \"unexpected keystore suffix\" log as a bug report trigger, not something to suppress"],"tags":["go","datastore","keystore","validation"],"backgroundTag":"invalid-keystore-suffix","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"}