ipfs/kubo · error
got unexpected number of keys back
Error message
got unexpected number of keys back
What it means
KeyAPI.Remove issues key/rm and asserts the daemon responds with exactly one removed key entry. Any other count (zero, or more than one) indicates the daemon did not do what was asked, so Remove fails rather than guessing which key was removed.
Source
Thrown at client/rpc/key.go:137
func (api *KeyAPI) Self(ctx context.Context) (iface.Key, error) {
var id struct{ ID string }
if err := api.core().Request("id").Exec(ctx, &id); err != nil {
return nil, err
}
return newKey("self", id.ID)
}
func (api *KeyAPI) Remove(ctx context.Context, name string) (iface.Key, error) {
var out struct {
Keys []keyOutput
}
if err := api.core().Request("key/rm", name).Exec(ctx, &out); err != nil {
return nil, err
}
if len(out.Keys) != 1 {
return nil, errors.New("got unexpected number of keys back")
}
return newKey(out.Keys[0].Name, out.Keys[0].Id)
}
func (api *KeyAPI) core() *HttpApi {
return (*HttpApi)(api)
}
func (api *KeyAPI) Sign(ctx context.Context, name string, data []byte) (iface.Key, []byte, error) {
var out struct {
Key keyOutput
Signature string
}
err := api.core().Request("key/sign").
Option("key", name).
FileBody(bytes.NewReader(data)).View on GitHub (pinned to 329838acdf)
Solutions
- Check the key exists first with api.Key().List(ctx) and only remove names present in that list.
- Make removals idempotent: treat a second Remove of the same name as success by pre-checking or catching the error.
- Upgrade the kubo daemon (and go-ipfs-api) to matching versions so the key/rm response shape matches the parser.
Example fix
// before
_, err := api.Key().Remove(ctx, "publish-key")
// after
keys, _ := api.Key().List(ctx)
if slices.ContainsFunc(keys, func(k ciutil.Key) bool { return k.Name() == "publish-key" }) {
_, err = api.Key().Remove(ctx, "publish-key")
} Defensive patterns
Strategy: validation
Validate before calling
keys, err := api.Key().List(ctx)
if err != nil { return err }
exists := slices.ContainsFunc(keys, func(k ciutil.Key) bool { return k.Name() == name })
if !exists {
return nil // nothing to remove; skip Remove entirely
} Prevention
- Check Key().List before removing so the key is known to exist
- Design removal as idempotent; tolerate 'key already gone' outcomes
- Avoid concurrent Remove calls for the same key name
- Pin kubo daemon and client versions together
When it happens
Trigger: Calling api.Key().Remove(ctx, name) where the daemon returns an empty Keys array (key already gone / never existed / racy double-remove) or, on mismatched daemon versions, a response shape the client parses into a different-length slice.
Common situations: Removing a key that was already deleted (e.g. retried request, concurrent caller), a name typo so nothing matched, or an old kubo daemon whose key/rm response format differs from what this client expects.
Related errors
- http api returned no error and no results
- unknowm mhType %d
- cids didn't match - local %s, remote %s
- msg (server-provided error message)
- Name.Resolve: depth other than 1 or %d not supported
AI-assisted analysis of ipfs/kubo@329838acdf (2026-09-03).
Data as JSON: /api/errors/01a25cb3389d22b9.
Report an issue: GitHub.