nats-io/nats-server · error

import forms a cycle

Error message

import forms a cycle

What it means

ErrImportFormsCycle is returned when configuring imports would create a cycle: an import chain leads back to an already-visited account, meaning messages could route in a loop. During import validation the server walks the import graph across accounts (server/accounts.go:1716 and :1776) and checks a `visited` set; if the next account in the chain was already seen, it declares a cycle. It is also exercised by cycle-detection tests like TestAccountCycleService.

Source

Thrown at server/errors.go:150

	ErrAccountResolverUpdateTooSoon = errors.New("account resolver update too soon")

	// ErrAccountResolverSameClaims is returned when same claims have been fetched.
	ErrAccountResolverSameClaims = errors.New("account resolver no new claims")

	// ErrStreamImportAuthorization is returned when a stream import is not authorized.
	ErrStreamImportAuthorization = errors.New("stream import not authorized")

	// ErrStreamImportBadPrefix is returned when a stream import prefix contains wildcards.
	ErrStreamImportBadPrefix = errors.New("stream import prefix can not contain wildcard tokens")

	// ErrStreamImportDuplicate is returned when a stream import is a duplicate of one that already exists.
	ErrStreamImportDuplicate = errors.New("stream import already exists")

	// ErrServiceImportAuthorization is returned when a service import is not authorized.
	ErrServiceImportAuthorization = errors.New("service import not authorized")

	// ErrImportFormsCycle is returned when an import would form a cycle.
	ErrImportFormsCycle = errors.New("import forms a cycle")

	// ErrCycleSearchDepth is returned when we have exceeded our maximum search depth..
	ErrCycleSearchDepth = errors.New("search cycle depth exhausted")

	// ErrClientOrRouteConnectedToGatewayPort represents an error condition when
	// a client or route attempted to connect to the Gateway port.
	ErrClientOrRouteConnectedToGatewayPort = errors.New("attempted to connect to gateway port")

	// ErrWrongGateway represents an error condition when a server receives a connect
	// request from a remote Gateway with a destination name that does not match the server's
	// Gateway's name.
	ErrWrongGateway = errors.New("wrong gateway")

	// ErrGatewayNameHasSpaces signals that the gateway name contains spaces, which is not allowed.
	ErrGatewayNameHasSpaces = errors.New("gateway name cannot contain spaces")

	// ErrNoSysAccount is returned when an attempt to publish or subscribe is made
	// when there is no internal system account defined.

View on GitHub (pinned to 3a66a489d2)

Solutions

  1. Break the loop: remove or redirect the import (or mapping) that points back to an account already in the chain.
  2. Redesign the chain so exports/imports form a directed acyclic graph, e.g. have B consume A but not re-export A's subject back.
  3. Draw the account import/export graph (or script it from the account JWTs) before adding new imports.
  4. If a relay is genuinely needed, use a distinct subject namespace on the middle account so the original subject is not re-exported.

Example fix

// before (cycle: A imports B, B imports A's same subject)
// A: import svc.* from B ; B: import svc.* from A
// after (acyclic)
// A: import svc.* from B ; B: no import of A's svc.* namespace
Defensive patterns

Strategy: validation

Validate before calling

// Validate the account import graph is acyclic before applying configs:
// parse each account's imports/exports, build edges acc(from)->acc(to), and run a DFS
// keeping a visited set; reject the change if any back-edge is found.

Try / catch

if err := applyAccountConfig(); err != nil {
    if errors.Is(err, ErrImportFormsCycle) {
        // log the import chain and roll back the config change
    }
}

Prevention

When it happens

Trigger: Declaring an import on account A that (directly or transitively through B, C, ...) points back to A; two accounts mutually importing each other's exports; a longer chain closed by a mapping (`subject_mapping`) that re-enters a visited account.

Common situations: Dev/test environments where accounts mirror each other's exports for convenience; incremental config edits that accidentally close a loop; renaming/renaming-mapping imports creating self-references.

Related errors


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