micro/go-micro · error
could not get next
Error message
could not get next
What it means
etcdWatcher.Next() receives watch events over a channel. When the server sends a watch response marked Canceled (the watch was closed server-side, e.g. the watched lease expired or the watch was compacted/closed), Next returns 'could not get next' instead of a result.
Source
Thrown at registry/etcd/watcher.go:52
if len(wo.Service) > 0 {
watchPath = servicePath(wo.Service) + "/"
}
return &etcdWatcher{
stop: stop,
w: r.client.Watch(ctx, watchPath, clientv3.WithPrefix(), clientv3.WithPrevKV()),
client: r.client,
timeout: timeout,
}, nil
}
func (ew *etcdWatcher) Next() (*registry.Result, error) {
for wresp := range ew.w {
if wresp.Err() != nil {
return nil, wresp.Err()
}
if wresp.Canceled {
return nil, errors.New("could not get next")
}
for _, ev := range wresp.Events {
service := decode(ev.Kv.Value)
var action string
switch ev.Type {
case clientv3.EventTypePut:
if ev.IsCreate() {
action = "create"
} else if ev.IsModify() {
action = "update"
}
case clientv3.EventTypeDelete:
action = "delete"
// get service from prevKv
service = decode(ev.PrevKv.Value)
}View on GitHub (pinned to 24529f1404)
Solutions
- Create a new watcher via registry.Watch() and resume watching when this error occurs
- Check etcd server logs for compaction or permission errors that canceled the watch
- Re-register the service / renew leases so the watched keys stay alive
- Wrap Next in a loop that treats this error as a signal to reconnect rather than abort
Example fix
// before
for {
r, err := w.Next()
if err != nil { return err } // watcher dies permanently
}
// after
for {
r, err := w.Next()
if err != nil {
time.Sleep(time.Second)
w, err = registry.Watch() // recreate watcher and continue
continue
}
handle(r)
} Defensive patterns
Strategy: retry
Try / catch
for {
r, err := w.Next()
if err != nil {
log.Printf("watch broken: %v; restarting", err)
time.Sleep(backoff)
w = newWatcher() // recreate after cancellation
continue
}
handle(r)
} Prevention
- Wrap watcher loops in reconnect-with-backoff logic
- Monitor etcd for compaction and permission changes
- Keep service leases renewed to avoid watch teardown
When it happens
Trigger: Iterating watcher.Next() while the underlying etcd watch is canceled — etcd returns wresp.Canceled=true (e.g. watch on a compacted revision, permission revoked, or server closing the watch) — and the code returns the error for that response.
Common situations: Long-running watchers surviving etcd cluster restarts or compaction; watching a service whose registration lease expired causing the watch to be torn down; auth token/permission changes mid-watch.
Related errors
AI-assisted analysis of micro/go-micro@24529f1404 (2026-09-01).
Data as JSON: /api/errors/2bc683b9b6fe944d.
Report an issue: GitHub.