nats-io/nats-server · error

stream republish transform from

Error message

stream republish transform from '%s' to '%s': %w

What it means

This error wraps a failure from NewSubjectTransform when a stream's RePublish configuration contains a subject transform that cannot be built. The server rejects stream creation/update rather than starting a stream whose republishing would silently drop or misroute messages. The wrapped error identifies the specific problem with the Source or Destination subject.

Solutions

  1. Fix the RePublish.Source subject so it is a valid NATS subject (no spaces, wildcard rules respected).
  2. Ensure RePublish.Destination wildcard tokens only reference tokens present in the Source (e.g. Source 'foo.*.bar' -> Destination 'baz.*' not 'baz.>').
  3. Print the wrapped %w error to see the exact subject-transform failure from NewSubjectTransform.
  4. Test subject strings with a validator before assigning them to cfg.RePublish.

Example fix

// before
cfg := jetstream.StreamConfig{
  Name: "ORDERS",
  RePublish: &jetstream.RePublish{Source: "orders..", Destination: "rep.>"},
}
// after
cfg := jetstream.StreamConfig{
  Name: "ORDERS",
  RePublish: &jetstream.RePublish{Source: "orders.*", Destination: "rep.orders"},
}
Defensive patterns

Strategy: validation

Validate before calling

func validRepublish(rp *jetstream.RePublish) error {
  if rp == nil { return nil }
  for _, s := range []string{rp.Source, rp.Destination} {
    if s == "" || strings.ContainsAny(s, " \t") { return fmt.Errorf("invalid subject %q", s) }
  }
  srcTok := strings.Split(rp.Source, ".")
  for i, t := range strings.Split(rp.Destination, ".") {
    if (t == "*" || t == ">") && (i >= len(srcTok) || (srcTok[i] != "*" && srcTok[i] != ">")) {
      return fmt.Errorf("destination wildcard at %d has no source counterpart", i)
    }
  }
  return nil
}

Type guard

func hasRepublish(cfg jetstream.StreamConfig) bool { return cfg.RePublish != nil }

Prevention

When it happens

Trigger: Calling StreamConfig.AddStream (or updating) with cfg.RePublish set to a RePublish{Source, Destination} whose Source is an invalid subject (wildcards misplaced, tokens >1 char) or whose Destination is not a valid transform target (e.g. contains wildcard tokens the Source does not provide).

Common situations: Typos in republish subjects; using '*' or '>' in the Destination that the Source doesn't supply; copy-pasting subjects with spaces; constructing subjects dynamically from user input; NATS server version differences in transform validation rules.

Related errors


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

Appendix: source

Thrown at server/stream.go:1063

		mset.ackq = newIPQueue[uint64](s, qpfx+"acks")
	}

	// Check for input subject transform
	if cfg.SubjectTransform != nil {
		tr, err := NewSubjectTransform(cfg.SubjectTransform.Source, cfg.SubjectTransform.Destination)
		if err != nil {
			jsa.mu.Unlock()
			return nil, fmt.Errorf("stream subject transform from '%s' to '%s': %w", cfg.SubjectTransform.Source, cfg.SubjectTransform.Destination, err)
		}
		mset.itr = tr
	}

	// Check for RePublish.
	if cfg.RePublish != nil {
		tr, err := NewSubjectTransform(cfg.RePublish.Source, cfg.RePublish.Destination)
		if err != nil {
			jsa.mu.Unlock()
			return nil, fmt.Errorf("stream republish transform from '%s' to '%s': %w", cfg.RePublish.Source, cfg.RePublish.Destination, err)
		}
		// Assign our transform for republishing.
		mset.tr = tr
	}
	storeDir := filepath.Join(jsa.storeDir, streamsDir, cfg.Name)
	jsa.mu.Unlock()

	// Bind to the user account.
	c.registerWithAccount(a)
	// Bind to the system account.
	ic.registerWithAccount(s.SystemAccount())

	// Create the appropriate storage
	fsCfg := fsConfig
	if fsCfg == nil {
		fsCfg = &FileStoreConfig{}
		// If we are file based and not explicitly configured
		// we may be able to auto-tune based on max msgs or bytes.

View on GitHub (pinned to 3a66a489d2)