ipfs/kubo · error

unexpected keystore suffix

Error message

unexpected keystore suffix %q, expected "0" or "1"

What it means

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.

Solutions

  1. Check the version of the upstream provider library in go.mod against what kubo expects; align it (go get <lib>@<pin> + make mod_tidy).
  2. Inspect the keystore directories under the repo datastore path; remove or rename any directory whose suffix is not "0" or "1" (after backing up).
  3. Search the code path computing the suffix for a regression and fix the calculation so only "0"/"1" are produced.
  4. If neither suffix applies, this is a hard stop by design — do not bypass the guard; report the upstream scheme change to kubo/boxo.

Example fix

// before
suffix := fmt.Sprint(counter) // may yield "2", "3", ...
if err := validateKeystoreSuffix(suffix); err != nil { ... }
// after
suffix := "0"
if counter%2 == 1 {
    suffix = "1"
}
if err := validateKeystoreSuffix(suffix); err != nil { ... }
Defensive patterns

Strategy: validation

Validate before calling

func safeKeystoreSuffix(n int) (string, error) {
    s := strconv.Itoa(n % 2)
    if s != "0" && s != "1" {
        return "", fmt.Errorf("computed suffix %q outside {0,1}", s)
    }
    return s, nil
}

Type guard

func isValidKeystoreSuffix(s string) bool { return s == "0" || s == "1" }

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of ipfs/kubo@329838acdf (2026-09-03). Data as JSON: /api/errors/a98fbab1d4fc4d48. Report an issue: GitHub.

Appendix: source

Thrown at core/node/provider.go:89

	keystoreDatastoreKey = datastore.NewKey("keystore")
)

// providerLog is the go-log subsystem used for provide/reprovide-related
// messages emitted from kubo's own orchestration code. It shares the
// "provider" subsystem name with boxo's provider package so users can set
// GOLOG_LOG_LEVEL=provider=<level> to control both layers at once. See
// docs/debug-guide.md for the full list of provide-related subsystems.
var providerLog = log.Logger("provider")

var errAcceleratedDHTNotReady = errors.New("AcceleratedDHTClient: routing table not ready")

// validateKeystoreSuffix rejects any suffix other than "0" or "1".
// The upstream library uses these two values as alternating namespace
// identifiers. Validating here prevents accidental deletion of unrelated
// directories via os.RemoveAll if the upstream ever changes its scheme.
func validateKeystoreSuffix(suffix string) error {
	if suffix != "0" && suffix != "1" {
		return fmt.Errorf("unexpected keystore suffix %q, expected \"0\" or \"1\"", suffix)
	}
	return nil
}

// Interval between reprovide queue monitoring checks for slow reprovide alerts.
// Used when Provide.DHT.SweepEnabled=true
const reprovideAlertPollInterval = 15 * time.Minute

// Number of consecutive polling intervals with sustained queue growth before
// triggering a slow reprovide alert (3 intervals = 45 minutes).
// Used when Provide.DHT.SweepEnabled=true
const consecutiveAlertsThreshold = 3

// DHTProvider is an interface for providing keys to a DHT swarm. It holds a
// state of keys to be advertised, and is responsible for periodically
// publishing provider records for these keys to the DHT swarm before the
// records expire.
type DHTProvider interface {

View on GitHub (pinned to 329838acdf)