nats-io/nats-server · error

subject transform from '%s' to '%s' for the source: %w

Error message

subject transform from '%s' to '%s' for the source: %w

What it means

Stream-creation validation for Sources: a Source.SubjectTransforms entry could not be constructed via NewSubjectTransform(source, destination) — the source/destination pair is not a valid subject transform (bad subject syntax or invalid wildcard usage in the mapping). The failing pair and underlying transform error are echoed.

Source

Thrown at server/stream.go:976

	// Setup our internal indexed names here for sources and check if the transforms (if any) are valid.
	for _, ssi := range cfg.Sources {
		if len(ssi.SubjectTransforms) == 0 {
			// check the filter, if any, is valid
			if ssi.FilterSubject != _EMPTY_ && !IsValidSubject(ssi.FilterSubject) {
				jsa.mu.Unlock()
				return nil, fmt.Errorf("subject filter '%s' for the source: %w", ssi.FilterSubject, ErrBadSubject)
			}
		} else {
			for _, st := range ssi.SubjectTransforms {
				if st.Source != _EMPTY_ && !IsValidSubject(st.Source) {
					jsa.mu.Unlock()
					return nil, fmt.Errorf("subject filter '%s' for the source: %w", st.Source, ErrBadSubject)
				}
				// check the transform, if any, is valid
				if st.Destination != _EMPTY_ {
					if _, err = NewSubjectTransform(st.Source, st.Destination); err != nil {
						jsa.mu.Unlock()
						return nil, fmt.Errorf("subject transform from '%s' to '%s' for the source: %w", st.Source, st.Destination, err)
					}
				}
			}
		}
	}

	// Check for overlapping subjects with other streams.
	// These are not allowed for now.
	if jsa.subjectsOverlap(cfg.Subjects, nil) {
		jsa.mu.Unlock()
		return nil, NewJSStreamSubjectOverlapError()
	}

	// Setup the internal clients.
	c := s.createInternalJetStreamClient()
	ic := s.createInternalJetStreamClient()

	// Work out the stream ingest limits.

View on GitHub (pinned to 3a66a489d2)

Solutions

  1. Make source and destination token structures compatible (wildcards map 1:1).
  2. Run NewSubjectTransform locally to reproduce the exact reason.
  3. Empty the destination if no transform is needed for that source.

Example fix

// before
{Source: "a.*", Destination: "b.*.c"}
// after
{Source: "a.*", Destination: "b.*"}
Defensive patterns

Strategy: validation

Validate before calling

for _, s := range cfg.Sources {
  for _, st := range s.SubjectTransforms {
    if st.Destination != "" {
      if _, err := server.NewSubjectTransform(st.Source, st.Destination); err != nil { return err }
    }
  }
}

Try / catch

_, err := js.AddStream(cfg)
if err != nil && strings.Contains(err.Error(), "subject transform from") { /* fix token mapping, resubmit */ }

Prevention

When it happens

Trigger: AddStream/UpdateStream where cfg.Sources[i].SubjectTransforms has a Destination that does not map from its Source (token-count or wildcard mismatch).

Common situations: Rewriting subjects across sourced streams with mismatched patterns, e.g. source "a.*" mapped to a three-token destination.

Related errors


AI-assisted analysis of nats-io/nats-server@3a66a489d2 (2026-09-02). Data as JSON: /api/errors/a0c081b620a8d5b9. Report an issue: GitHub.