go-kratos/kratos · error

register failed: instance duplicated:

Error message

register failed: instance duplicated: 

What it means

ErrDuplication is defined in contrib/registry/discovery/discovery_helper.go:16 and returned by Discovery.Register (impl_registrar.go:21) when the same Discovery client already has an entry for this AppID in its in-memory registry map; the error is wrapped with the AppID (errors.Wrap(ErrDuplication, ins.AppID)), so the message ends with the duplicated app id. It is a local double-registration guard evaluated before any HTTP call to the discovery server.

Source

Thrown at contrib/registry/discovery/discovery_helper.go:16

package discovery

import (
	"fmt"
	"net/url"
	"os"
	"strconv"
	"time"

	"github.com/pkg/errors"

	"github.com/go-kratos/kratos/v3/registry"
)

var (
	ErrDuplication = errors.New("register failed: instance duplicated: ")
	ErrServerError = errors.New("server error")
)

const (
	// Discovery server resource uri
	_registerURL = "http://%s/discovery/register"
	//_setURL      = "http://%s/discovery/set"
	_cancelURL = "http://%s/discovery/cancel"
	_renewURL  = "http://%s/discovery/renew"
	_pollURL   = "http://%s/discovery/polls"

	// Discovery server error codes
	_codeOK          = 0
	_codeNotFound    = -404
	_codeNotModified = -304
	//_SERVER_ERROR = -500

	// _registerGap is the gap to renew instance registration.

View on GitHub (pinned to 668db92c2c)

Solutions

  1. Register each AppID exactly once per Discovery client; rely on kratos' app lifecycle instead of calling Register manually
  2. If you must re-register, call Deregister (or Close the old client) first so the map entry is cleared
  3. Guard the registration with a sync.Once or a startup flag
  4. Match it with errors.Is(err, discovery.ErrDuplication) since pkg/errors Wrap supports unwrapping

Example fix

// before
var once sync.Once
for _, ins := range instances {
    if err := d.Register(ctx, ins); err != nil { log.Error(err) }
}

// after
register := sync.OnceFunc(func() {
    if err := d.Register(ctx, svc); err != nil {
        if errors.Is(err, discovery.ErrDuplication) { return }
        log.Error(err)
    }
})
Defensive patterns

Strategy: try-catch

Validate before calling

// guard before registering: ensure one registration per app id per client
if d == nil || svc.Name == "" { return errors.New("invalid registration") }
// rely on kratos lifecycle; if registering manually, wrap in sync.Once

Try / catch

if err := d.Register(ctx, svc); err != nil {
    if errors.Is(err, discovery.ErrDuplication) {
        // already registered on this client: benign for idempotent startup paths
        err = nil
    }
    return err
}

Prevention

When it happens

Trigger: Calling d.Register(ctx, &registry.ServiceInstance{...}) twice with the same service name on the same Discovery instance - the second call hits the 'if _, ok := d.registry[ins.AppID]' branch. Also happens when re-registering after a partial failure without calling Deregister or Close on the client first.

Common situations: Hot-reload/restart logic that re-runs the registration path on the same long-lived Discovery value; multiple goroutines registering the same app at startup; retry wrapper around Register that re-enters after the first call already succeeded; mixing manual Register with the kratos registrar lifecycle (kratos registers automatically at server start).

Related errors


AI-assisted analysis of go-kratos/kratos@668db92c2c (2026-08-16). Data as JSON: /api/errors/af614d5ccf40a35a. Report an issue: GitHub.