dapr/dapr · error

remote actor moved

Error message

remote actor moved

What it means

GenerateJWT persists the signing key by marshalling it to PKCS8 (x509.MarshalPKCS8PrivateKey) and PEM-encoding it. MarshalPKCS8 supports RSA, ECDSA, Ed25519, and X25519 keys; any other crypto.Signer type — notably custom signers or unsupported algorithms — yields 'failed to marshal JWT signing key'. As above, the in-repo caller passes a generated RSA key, so this guards against unsupported key types from external callers.

Source

Thrown at pkg/actors/router/router.go:200

func (r *router) callReminder(ctx context.Context, req *api.Reminder) error {
	lar, cctx, cancel, err := r.placement.LookupActor(ctx, &api.LookupActorRequest{
		ActorType: req.ActorType,
		ActorID:   req.ActorID,
	})
	if err != nil {
		return err
	}

	if req.SkipLock || !lar.Local {
		cancel(nil)
	} else {
		defer cancel(nil)
		ctx = cctx
	}

	if !lar.Local {
		if req.IsRemote {
			return backoff.Permanent(errors.New("remote actor moved"))
		}

		err = r.callRemoteActorReminder(ctx, lar, req)
		status, ok := status.FromError(err)
		if ok && status.Code() == codes.Unavailable {
			return backoff.Permanent(err)
		}
		return err
	}

	target, err := r.table.GetOrCreate(req.ActorType, req.ActorID)
	if err != nil {
		return backoff.Permanent(err)
	}

	if req.IsTimer {
		err = target.InvokeTimer(ctx, req)
	} else {

View on GitHub (pinned to 74ad417027)

Solutions

  1. Use one of RSA/ECDSA/Ed25519 (or X25519) private keys as the JWT root key.
  2. If you need another algorithm, serialize it yourself rather than via GenerateJWT.
  3. Check the wrapped error to confirm the type rejection.
  4. Keep the stored jwt.key as PKCS8 PEM so future loads round-trip.

Example fix

// before: custom signer
type mySigner struct{ crypto.Signer }
bundle.GenerateJWT(bundle.OptionsJWT{JWTRootKey: mySigner{...}})
// after
rsaKey, _ := rsa.GenerateKey(rand.Reader, 2048)
bundle.GenerateJWT(bundle.OptionsJWT{JWTRootKey: rsaKey})
Defensive patterns

Strategy: type-guard

Validate before calling

switch opts.JWTRootKey.(type) {
case *rsa.PrivateKey, *ecdsa.PrivateKey, ed25519.PrivateKey:
default:
	return fmt.Errorf("key type %T cannot be PKCS8-marshaled by GenerateJWT", opts.JWTRootKey)
}

Type guard

func isPKCS8Marshalable(k any) bool {
	switch k.(type) {
	case *rsa.PrivateKey, *ecdsa.PrivateKey, ed25519.PrivateKey, x25519.PrivateKey:
		return true
	}
	return false
}

Try / catch

j, err := bundle.GenerateJWT(opts)
if err != nil && strings.Contains(err.Error(), "failed to marshal JWT signing key") {
	// swap in a supported key type; custom Signers cannot be persisted here
}

Prevention

When it happens

Trigger: OptionsJWT.JWTRootKey is a key type outside PKCS8's supported set (e.g., a DSA key or a wrapper Signer that is not one of the four supported concrete types).

Common situations: Forks adding algorithms like ML-DSA or custom HSM-backed signers; test harnesses passing mock signers.

Related errors


AI-assisted analysis of dapr/dapr@74ad417027 (2026-08-16). Data as JSON: /api/errors/1cf9c0d26704064c. Report an issue: GitHub.