wavetermdev/waveterm · error

error setting jwt public key: %v

Error message

error setting jwt public key: %v

What it means

This error is wrapped by serverRunRouter when wavejwt.SetPublicKey fails after the base64-encoded JWT public key was fetched from the upstream connserver over the stdio-based router. SetPublicKey rejects keys that are not a valid public key it can parse (e.g. not PEM/DER-encoded RSA or ECDSA material), so the remote connection cannot proceed with JWT verification. The wrapped %v carries the underlying key-parsing error.

Source

Thrown at cmd/wsh/cmd/wshcmd-connserver.go:259

		return fmt.Errorf("error setting up connserver rpc client: %v", err)
	}
	wshfs.RpcClient = client
	wshfs.RpcClientRouteId = bareRouteId

	log.Printf("trying to get JWT public key")

	// fetch and set JWT public key
	jwtPublicKeyB64, err := wshclient.GetJwtPublicKeyCommand(client, nil)
	if err != nil {
		return fmt.Errorf("error getting jwt public key: %v", err)
	}
	jwtPublicKeyBytes, err := base64.StdEncoding.DecodeString(jwtPublicKeyB64)
	if err != nil {
		return fmt.Errorf("error decoding jwt public key: %v", err)
	}
	err = wavejwt.SetPublicKey(jwtPublicKeyBytes)
	if err != nil {
		return fmt.Errorf("error setting jwt public key: %v", err)
	}

	log.Printf("got JWT public key")

	// now set up the domain socket
	unixListener, err := MakeRemoteUnixListener()
	if err != nil {
		return fmt.Errorf("cannot create unix listener: %v", err)
	}
	log.Printf("unix listener started")
	go func() {
		defer func() {
			panichandler.PanicHandler("serverRunRouter:runListener", recover())
		}()
		runListener(unixListener, router)
	}()
	// run the sysinfo loop
	go func() {

View on GitHub (pinned to a4447c1563)

Solutions

  1. Check wsh and waveterm server versions match (upgrade both to the same release).
  2. Log the base64 key before decoding and verify it decodes to valid PEM public key material.
  3. Re-run the connection so GetJwtPublicKeyCommand returns a fresh key from the real upstream.
  4. If running a custom/proxy upstream, return the exact key bytes produced by the server's JWT signer.

Example fix

// before
jwtPublicKeyBytes, err := base64.StdEncoding.DecodeString(jwtPublicKeyB64)
if err != nil {
	return fmt.Errorf("error decoding jwt public key: %v", err)
}
// after
jwtPublicKeyBytes, err := base64.StdEncoding.DecodeString(jwtPublicKeyB64)
if err != nil {
	return fmt.Errorf("error decoding jwt public key: %v", err)
}
log.Printf("jwt public key (b64, len=%d): %s", len(jwtPublicKeyB64), jwtPublicKeyB64) // inspect payload
if err := wavejwt.SetPublicKey(jwtPublicKeyBytes); err != nil {
	return fmt.Errorf("error setting jwt public key (key bytes=%d): %w", len(jwtPublicKeyBytes), err)
}
Defensive patterns

Strategy: validation

Validate before calling

decoded, err := base64.StdEncoding.DecodeString(jwtPublicKeyB64)
if err != nil || len(decoded) == 0 {
	return fmt.Errorf("bad jwt public key payload")
}
if !bytes.Contains(decoded, []byte("PUBLIC KEY")) {
	return fmt.Errorf("payload is not PEM public key material")
}

Type guard

func isValidPublicKeyPem(b []byte) bool {
	block, _ := pem.Decode(b)
	return block != nil && strings.HasSuffix(block.Type, "PUBLIC KEY")
}

Try / catch

if err := wavejwt.SetPublicKey(keyBytes); err != nil {
	return fmt.Errorf("error setting jwt public key: %w", err)
}

Prevention

When it happens

Trigger: wsh connserver run in router mode (upstream over stdin/stdout) calls GetJwtPublicKeyCommand, base64-decodes it successfully, then wavejwt.SetPublicKey(jwtPublicKeyBytes) returns an error because the bytes are not a parseable public key.

Common situations: Version mismatch between wsh client and waveterm server where the key format changed; a corrupted or truncated key in transit; a stub/mock upstream returning garbage for GetJwtPublicKey; hand-edited JWT key configuration.

Related errors


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