temporalio/temporal · error
ErrInvalidOperationToken
ErrInvalidOperationToken
Error message
%w: length exceeds allowed limit (%d/%d)
What it means
When a Nexus operation handler returns a start result containing a pending-operations token, the token length must not exceed the configured MaxOperationTokenLength for the namespace. If it does, the result is rejected with ErrInvalidOperationToken wrapped with the actual and allowed lengths. This protects history from persisting oversized tokens.
Source
Thrown at chasm/lib/nexusoperation/operation_tasks.go:250
// This happens when we accept the ScheduleNexusOperation command when the endpoint is not found in the
// registry as indicated by the EndpointNotFoundAlwaysNonRetryable dynamic config.
// The config has been removed but we keep this check for backward compatibility.
if args.endpointID == "" {
return nil, nexus.NewHandlerErrorf(nexus.HandlerErrorTypeNotFound, "endpoint not registered")
}
return lookupEndpoint(ctx, h.endpointRegistry, ns.ID(), args.endpointID, args.endpointName)
}
func (h *operationInvocationTaskHandler) validateStartResult(
ns *namespace.Namespace,
result *nexusrpc.ClientStartOperationResponse[*commonpb.Payload],
) error {
if result == nil {
return nil
}
tokenLimit := h.config.MaxOperationTokenLength(ns.Name().String())
if result.Pending != nil && len(result.Pending.Token) > tokenLimit {
return fmt.Errorf("%w: length exceeds allowed limit (%d/%d)", ErrInvalidOperationToken, len(result.Pending.Token), tokenLimit)
}
if result.Successful != nil && result.Successful.Size() > h.config.PayloadSizeLimit(ns.Name().String()) {
return ErrResponseBodyTooLarge
}
return nil
}
type operationBackoffTaskHandler struct {
chasm.PureTaskHandlerBase
config *Config
metricsHandler metrics.Handler
logger log.Logger
}
func newOperationBackoffTaskHandler(opts operationTaskHandlerOptions) *operationBackoffTaskHandler {
return &operationBackoffTaskHandler{
config: opts.Config,View on GitHub (pinned to bde624efd1)
Solutions
- Reduce the token size returned by the handler: keep it a compact opaque reference and store state externally or in the operation component
- Raise nexusoperation.pending token limit config (MaxOperationTokenLength) for the namespace if the current token size is legitimate
- Log/compute len(token) in the handler and compare against the configured limit before returning
- Check for accidental inclusion of large payloads (base64 blobs, full request bodies) in the token
Example fix
// before
return &nexus.OperationStartResult{Pending: &nexus.OperationPending{Token: bigToken}}, nil
// after
if len(token) > maxTokenLen { token = compactToken(token) }
return &nexus.OperationStartResult{Pending: &nexus.OperationPending{Token: token}}, nil Defensive patterns
Strategy: validation
Validate before calling
tokenLimit := h.config.MaxOperationTokenLength(ns)
if len(result.Pending.Token) > tokenLimit {
return fmt.Errorf("token too long: %d/%d", len(result.Pending.Token), tokenLimit)
}
if result.Successful.Size() > h.config.PayloadSizeLimit(ns) {
return errors.New("response body too large")
} Try / catch
// Go
err := operation.Execute(ctx, req)
if errors.Is(err, ErrInvalidOperationToken) {
// shrink token and retry the handler result, or surface config mismatch
} Prevention
- Keep pending tokens small and opaque; persist large state outside the token
- Compute len(token) in handler tests against the lowest configured limit
- Sync MaxOperationTokenLength config across environments
- Never embed raw payloads or blobs in operation tokens
When it happens
Trigger: A Nexus operation handler returns nexus.OperationStartResult with result.Pending.Token longer than MaxOperationTokenLength (namespace-configured limit) — validateStartResult is called from Execute after the handler returns.
Common situations: A handler encodes too much state into the pending token (e.g. embedding large payloads/IDs); server operator lowers MaxOperationTokenLength below existing handler token sizes; namespace-specific config divergence between dev and prod.
Related errors
- link type is empty
- link type contains invalid char (valid chars: alphanumeric,
- empty operation name
- empty operation token
- ErrCancellationAlreadyRequested
AI-assisted analysis of temporalio/temporal@bde624efd1 (2026-09-01).
Data as JSON: /api/errors/64ca254f0a6041e4.
Report an issue: GitHub.