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
- Register each AppID exactly once per Discovery client; rely on kratos' app lifecycle instead of calling Register manually
- If you must re-register, call Deregister (or Close the old client) first so the map entry is cleared
- Guard the registration with a sync.Once or a startup flag
- 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
- Register each AppID once per Discovery client; let kratos' app lifecycle own registration
- Call Deregister or Close before any intentional re-registration
- Watch logs for the wrapped AppID in the message to identify which service double-registered
When it happens
Trigger: Calling d.Register(ctx, ®istry.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
- ErrorCode: %d
- Discovery.GetService fetch failed
- invalid Discovery config nodes:%+v region:%s zone:%s deployE
- Discovery.GetService(%s) not found
- iterator closed
AI-assisted analysis of go-kratos/kratos@668db92c2c (2026-08-16).
Data as JSON: /api/errors/af614d5ccf40a35a.
Report an issue: GitHub.