wavetermdev/waveterm · error

client id mismatch: expected %s, got %s

Error message

client id mismatch: expected %s, got %s

What it means

checkClientId enforces single-client semantics: once CurrentClientId is set, calls with a different id are rejected with this mismatch error. This detects a second client (or a restarted client with a new id) trying to act on the same ClientImpl.

Source

Thrown at tsunami/engine/clientimpl.go:137

}

func (c *ClientImpl) GetIsDone() bool {
	c.Lock.Lock()
	defer c.Lock.Unlock()
	return c.IsDone
}

func (c *ClientImpl) checkClientId(clientId string) error {
	if clientId == "" {
		return fmt.Errorf("client id cannot be empty")
	}
	c.Lock.Lock()
	defer c.Lock.Unlock()
	if c.CurrentClientId == "" || c.CurrentClientId == clientId {
		c.CurrentClientId = clientId
		return nil
	}
	return fmt.Errorf("client id mismatch: expected %s, got %s", c.CurrentClientId, clientId)
}

func (c *ClientImpl) clientTakeover(clientId string) {
	c.Lock.Lock()
	defer c.Lock.Unlock()
	c.CurrentClientId = clientId
}

func (c *ClientImpl) doShutdown(reason string) {
	c.Lock.Lock()
	defer c.Lock.Unlock()
	if c.IsDone {
		return
	}
	c.DoneReason = reason
	c.IsDone = true
	close(c.DoneCh)
}

View on GitHub (pinned to a4447c1563)

Solutions

  1. Reuse the same clientId consistently for the lifetime of the ClientImpl (store it once at first successful call)
  2. Call clientTakeover deliberately when a new client should replace the old one
  3. On mismatch, log both ids and reinitialize or restart the client cleanly

Example fix

// before
client.Do(newRandomUUID(), req) // new id every call
// after
if myClientID == "" { myClientID = newRandomUUID() }
client.Do(myClientID, req)
Defensive patterns

Strategy: validation

Validate before calling

var myClientID string
func ensureClientID(c *tsunami.ClientImpl, id string) (string, error) {
    if myClientID == "" { myClientID = id }
    if id != myClientID {
        return "", fmt.Errorf("must use client id %s, got %s", myClientID, id)
    }
    return myClientID, nil
}

Try / catch

if err := client.Send(id, msg); err != nil {
    if strings.Contains(err.Error(), "client id mismatch") {
        log.Printf("stale id %q; reinitializing client session", id)
        client = reinitClient()
    }
}

Prevention

When it happens

Trigger: Calling a client method with clientId X while CurrentClientId is Y (already claimed by a previous call). The claim is set on first successful checkClientId and only changed via clientTakeover.

Common situations: Reconnecting after a crash with a newly generated id while the old ClientImpl is still in use; two goroutines/processes sharing one client with different ids; stale cached id from a previous session.

Related errors


AI-assisted analysis of wavetermdev/waveterm@a4447c1563 (2026-09-01). Data as JSON: /api/errors/c61b0bb9c3ae9076. Report an issue: GitHub.