ipfs/kubo · error

invalid key

Error message

invalid key

What it means

normalizeKey validates an IPNS/pk routing key path, which must have the form /ipns/<peerid> or /pk/<peerid> (three slash-separated parts, empty first). Anything else — wrong prefix, missing parts, extra slashes — returns "invalid key" before the peer ID is even decoded. It is a strict path-format validation.

Source

Thrown at core/coreapi/routing.go:65

	err = api.checkOnline(options.AllowOffline)
	if err != nil {
		return err
	}

	dhtKey, err := normalizeKey(key)
	if err != nil {
		return err
	}

	return api.routing.PutValue(ctx, dhtKey, value)
}

func normalizeKey(s string) (string, error) {
	parts := strings.Split(s, "/")
	if len(parts) != 3 ||
		parts[0] != "" ||
		!(parts[1] == "ipns" || parts[1] == "pk") {
		return "", errors.New("invalid key")
	}

	k, err := peer.Decode(parts[2])
	if err != nil {
		return "", err
	}
	return strings.Join(append(parts[:2], string(k)), "/"), nil
}

func (api *RoutingAPI) FindPeer(ctx context.Context, p peer.ID) (peer.AddrInfo, error) {
	ctx, span := tracing.Span(ctx, "CoreAPI.DhtAPI", "FindPeer", trace.WithAttributes(attribute.String("peer", p.String())))
	defer span.End()
	err := api.checkOnline(false)
	if err != nil {
		return peer.AddrInfo{}, err
	}

	pi, err := api.routing.FindPeer(ctx, peer.ID(p))

View on GitHub (pinned to 329838acdf)

Solutions

  1. Format the key as /ipns/<peerID> or /pk/<peerID>, with leading slash and exactly three segments.
  2. Ensure the third segment decodes as a valid libp2p peer ID (use peer.Decode-compatible formats).
  3. Build the path with fmt.Sprintf("/ipns/%s", id) from a peer.ID rather than concatenating strings.
  4. If the value is a DNSLink name, resolve it to a peer ID first — DNS names are not valid here.

Example fix

// before
val, err := api.Routing().Get(ctx, "ipns/12D3KooW...")
// after
val, err := api.Routing().Get(ctx, "/ipns/12D3KooW...")
Defensive patterns

Strategy: validation

Validate before calling

func validRoutingKey(s string) bool {
	parts := strings.Split(s, "/")
	if len(parts) != 3 || parts[0] != "" || (parts[1] != "ipns" && parts[1] != "pk") {
		return false
	}
	_, err := peer.Decode(parts[2])
	return err == nil
}

Type guard

func toRoutingKey(id peer.ID) (string, bool) {
	if id == "" {
		return "", false
	}
	return "/ipns/" + id.String(), true
}

Try / catch

val, err := api.Routing().Get(ctx, key)
if err != nil && strings.Contains(err.Error(), "invalid key") {
	return fmt.Errorf("key %q must be /ipns/<peerid> or /pk/<peerid>: %w", key, err)
}

Prevention

When it happens

Trigger: Calling Routing().Get or Routing().Put with keys like "ipns/foo", "/ipns", "/ipfs/foo", "/pk/<id>/extra", or a raw peer id without the leading slash and namespace.

Common situations: Scripts that build the key path by hand and forget the leading slash, using the wrong namespace prefix (ipfs instead of ipns/pk), and passing CIDs or DNSNames where a peer ID is required.

Related errors


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