wavetermdev/waveterm · error

failed to unmarshal response: %w

Error message

failed to unmarshal response: %w

What it means

AuthenticateTokenCommand on a non-root router forwarded the token-verify request to the root and got a response, but utilfn.ReUnmarshal could not decode respData into CommandAuthenticateRtnData. This means the root's response body did not match the expected schema (wrong type, version skew, or error payload in the data slot).

Source

Thrown at pkg/wshutil/wshrouter_controlimpl.go:200

	if impl.Router.IsRootRouter() {
		rtnData, err = extractTokenData(data.Token)
		if err != nil {
			log.Printf("wshrouter authenticate-token error linkid=%d: %v", linkId, err)
			return wshrpc.CommandAuthenticateRtnData{}, err
		}
	} else {
		wshRpc := GetWshRpcFromContext(ctx)
		if wshRpc == nil {
			return wshrpc.CommandAuthenticateRtnData{}, fmt.Errorf("no wshrpc in context")
		}
		respData, err := wshRpc.SendRpcRequest(wshrpc.Command_AuthenticateTokenVerify, data, &wshrpc.RpcOpts{Route: ControlRootRoute})
		if err != nil {
			log.Printf("wshrouter authenticate-token error linkid=%d: failed to verify token: %v", linkId, err)
			return wshrpc.CommandAuthenticateRtnData{}, fmt.Errorf("failed to verify token: %w", err)
		}
		err = utilfn.ReUnmarshal(&rtnData, respData)
		if err != nil {
			return wshrpc.CommandAuthenticateRtnData{}, fmt.Errorf("failed to unmarshal response: %w", err)
		}
	}

	if rtnData.RpcContext == nil {
		return wshrpc.CommandAuthenticateRtnData{}, fmt.Errorf("no rpccontext in token response")
	}
	if rtnData.RouteId == "" {
		return wshrpc.CommandAuthenticateRtnData{}, fmt.Errorf("no routeid in token response")
	}
	log.Printf("wshrouter authenticate-token success linkid=%d routeid=%q", linkId, rtnData.RouteId)
	impl.Router.trustLink(linkId, LinkKind_Leaf)
	impl.Router.bindRoute(linkId, rtnData.RouteId, true)

	return rtnData, nil
}

func (impl *WshRouterControlImpl) AuthenticateJobManagerVerifyCommand(ctx context.Context, data wshrpc.CommandAuthenticateJobManagerData) error {
	if !impl.Router.IsRootRouter() {

View on GitHub (pinned to a4447c1563)

Solutions

  1. Log respData raw JSON to see what the root actually returned.
  2. Check for version skew between the two wshutil binaries and upgrade both to matching versions.
  3. Verify the root's AuthenticateTokenVerifyCommand returns a properly-typed CommandAuthenticateRtnData, not an error payload.
  4. Re-run after updating; the error is deterministic if schemas mismatch.

Example fix

// before
err = utilfn.ReUnmarshal(&rtnData, respData)
if err != nil {
    return wshrpc.CommandAuthenticateRtnData{}, fmt.Errorf("failed to unmarshal response: %w", err)
}
// after
err = utilfn.ReUnmarshal(&rtnData, respData)
if err != nil {
    log.Printf("authenticate-token: bad response payload: %#v", respData)
    return wshrpc.CommandAuthenticateRtnData{}, fmt.Errorf("failed to unmarshal response: %w", err)
}
Defensive patterns

Strategy: try-catch

Type guard

func isValidAuthRtnData(raw json.RawMessage) bool {
    var d wshrpc.CommandAuthenticateRtnData
    return json.Unmarshal(raw, &d) == nil && d.RouteId != ""
}

Try / catch

rtn, err := router.AuthenticateTokenCommand(ctx, data)
if err != nil && strings.Contains(err.Error(), "failed to unmarshal response") {
    log.Printf("schema mismatch in token-verify response, upgrade both peers: %v", err)
    return errUpgradeRequired
}

Prevention

When it happens

Trigger: SendRpcRequest(Command_AuthenticateTokenVerify) succeeds but respData cannot be re-marshalled into wshrpc.CommandAuthenticateRtnData — e.g. response is a raw/error object, field types changed, or an older/newer peer returns a different CommandAuthenticateRtnData shape.

Common situations: Version mismatch between client and root router where the RPC response schema changed; the root returning an error object as response data; JSON with a field of the wrong type (e.g. routeid as number instead of string).

Related errors


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