Checks/ACCOUNTS_003

NATS Inactive JWT Import: What It Means and How to Fix It

Severity
Critical
Category
Consistency
Applies to
Account
Check ID
ACCOUNTS_003
Detection threshold
JWT import not in active imports

An inactive JWT import means an account has declared an import in its JWT, but the server has not activated it. The account believes it has access to subjects from another account, but messages are silently not flowing. Insights diagnoses the root cause: missing activation token, expired token, token signed by a rotated signing key, or source export not found.

Why this matters

Inactive imports are one of the most insidious problems in multi-account NATS deployments because they fail silently. The importing account’s JWT declares the import, the subscribing clients subscribe to the local subject, and everything looks correct — but no messages arrive. There are no error messages on the subscribing client, no warnings in the publishing account, and no obvious indication in server logs unless you know where to look. The system behaves as if the subject simply has no publishers.

This silent failure mode makes inactive imports dangerous in production. A service that depends on data from another account via an import — pricing feeds, event streams, configuration updates — stops receiving data without any error signal. Depending on how the service handles missing data, it may serve stale results, make decisions based on incomplete information, or silently degrade. The failure can persist for hours or days before someone notices the data stopped flowing.

The problem is compounded by the lifecycle of JWT-based deployments. Activation tokens expire. Signing keys get rotated. Accounts are restructured. Exports are modified. Any of these routine operations can silently break an import that was working yesterday. Without continuous monitoring, the only way to discover the break is when an end user reports a problem downstream.

Common causes

  • Missing activation token. The exporting account has a private export (the default for service exports), which requires an activation token for each importing account. The import was configured without providing the token, so the server can’t verify the importing account is authorized to access the export.

  • Expired activation token. Activation tokens are JWTs and can have expiration dates. The token was valid when the import was configured, but has since expired. The server stops honoring the import once the token’s exp claim passes.

  • Token signed by a rotated signing key. The exporting account rotated its signing keys (a routine security practice). The activation token was signed by the old key, which the server no longer trusts. The import was valid under the old key hierarchy but is invalid under the new one.

  • Source export not found. The exporting account removed or renamed the export that this import references. The import declares a subject that no account currently exports. This can happen during account restructuring, subject namespace changes, or when the exporting account is reconfigured without updating downstream importers.

  • Source account not found. The exporting account no longer exists in the server’s account resolver. It was deleted, moved to a different operator, or the resolver lost its JWT. The import references an account ID that the server can’t resolve.

  • Subject mismatch. The import references a subject pattern that doesn’t exactly match the export’s declared subject. NATS subject matching is exact (with wildcard support), so orders.> doesn’t match an export of orders.us.> unless the export covers the broader pattern.

How to diagnose

Check the Insights diagnostic detail

Insights provides a specific diagnostic for each inactive import, categorized as one of:

  1. Missing activation token
  2. Expired activation token
  3. Token signed by rotated signing key
  4. Source export/account not found

This tells you exactly which remediation path to follow:

  • ‘Missing activation token’ — request an activation token from the exporting account operator.
  • ‘Expired activation token’ — renew the token with the exporting account.
  • ‘Activation token signed by rotated signing key’ — re-issue the token with the current signing key.
  • ‘Source export not found’ — verify the export exists in the exporting account JWT.

List account imports and their status

Terminal window
# Show the account JWT details, including imports
nsc describe account <account_name>

Look at the Imports section. Each import shows the source account, subject, type (stream or service), and whether an activation token is present.

Verify the export exists in the source account

Terminal window
# Check what the source account exports
nsc describe account <source_account_name>

Verify that the subject listed in the import matches an export in the source account. Pay attention to wildcard patterns — orders.> must match exactly.

Check activation token validity

Terminal window
# Validate the account configuration
nsc validate
# Describe the import to see token details
nsc describe account <account_name>

If the activation token is expired, the output will show the expiration date. If the token was signed by a rotated key, the validation will flag the signing key mismatch.

Verify the account is resolvable

Terminal window
# Check if the server can resolve the source account
nats server account info <source_account_name>

If this returns an error, the source account’s JWT is not available in the server’s resolver.

Check server logs for import activation errors

Terminal window
# Look for import-related warnings
journalctl -u nats-server --since "1 hour ago" | grep -i "import\|activation\|export"

The server logs import activation failures at startup and when account JWTs are updated.

How to fix it

Immediate: identify and fix the specific cause

For missing activation tokens, generate the token from the exporting account:

Terminal window
# Generate activation token for the importing account
nsc generate activation \
--account <export_account> \
--subject "orders.>" \
--target-account <import_account_public_key> \
--output-file activation.jwt
# Add the token to the importing account
nsc add import \
--account <import_account> \
--src-account <export_account_public_key> \
--remote-subject "orders.>" \
--token activation.jwt

For expired activation tokens, regenerate with a new expiration:

Terminal window
# Generate a new activation token (optionally set expiration)
nsc generate activation \
--account <export_account> \
--subject "orders.>" \
--target-account <import_account_public_key> \
--expiry 365d \
--output-file activation-renewed.jwt

Then update the import in the importing account’s JWT with the new token.

For tokens signed by rotated signing keys, regenerate using the current signing key:

Terminal window
# The new activation is automatically signed with the current key
nsc generate activation \
--account <export_account> \
--subject "orders.>" \
--target-account <import_account_public_key> \
--output-file activation-rekeyed.jwt

For missing exports, recreate the export in the source account or update the import to reference the correct subject:

Terminal window
# Add the export back to the source account
nsc add export \
--account <export_account> \
--subject "orders.>" \
--name "orders-stream"

Short-term: push updated JWTs

After fixing the import or activation token, push the updated account JWTs to the server:

Terminal window
# Push updated account JWT
nsc push -a <account_name>
# Verify the import is now active
nats server account info <account_name>
1
// Go client — verify import is working by subscribing to the imported subject
2
nc, err := nats.Connect(url, nats.UserCredentials("path/to/user.creds"))
3
if err != nil {
4
log.Fatal(err)
5
}
6
7
sub, err := nc.SubscribeSync("imported.orders.>")
8
if err != nil {
9
log.Fatal(err)
10
}
11
12
msg, err := sub.NextMsg(5 * time.Second)
13
if err != nil {
14
log.Printf("No messages received on imported subject — import may still be inactive: %v", err)
15
} else {
16
log.Printf("Import active, received: %s", string(msg.Data))
17
}
1
# Python — verify import is working
2
import nats
3
import asyncio
4
5
async def verify_import():
6
nc = await nats.connect("nats://localhost:4222", user_credentials="path/to/user.creds")
7
sub = await nc.subscribe("imported.orders.>")
8
9
try:
10
msg = await sub.next_msg(timeout=5)
11
print(f"Import active, received: {msg.data.decode()}")
12
except asyncio.TimeoutError:
13
print("No messages — import may still be inactive")
14
15
await nc.close()

Long-term: prevent inactive imports

Set up activation token lifecycle management. Track activation token expiration dates and renew them before they expire. This is especially important in large deployments with many cross-account imports:

Terminal window
# List all imports across accounts to audit
nsc list imports --account <account_name>
# Validate all accounts for issues
nsc validate

Coordinate signing key rotations. When rotating signing keys on an exporting account, regenerate all activation tokens for that account’s exports before or immediately after the rotation. Document which importing accounts need new tokens.

Use public exports where appropriate. If an export doesn’t need access control (it’s available to all accounts), declare it as a public export. Public exports don’t require activation tokens, eliminating the most common cause of inactive imports:

Terminal window
# Create a public export (no activation token required)
nsc add export \
--account <export_account> \
--subject "public.events.>" \
--public

Monitor continuously. Insights evaluates import activation status at every collection epoch. When a routine key rotation or token expiration breaks an import, the check fires immediately rather than waiting for a downstream user to notice missing data.

Frequently asked questions

Why don’t inactive imports produce client-side errors?

The NATS protocol doesn’t distinguish between “no messages because the subject has no publishers” and “no messages because your import is broken.” From the subscribing client’s perspective, it’s subscribed to a subject and no messages arrive — the same as subscribing to a subject that nobody publishes to. This is by design (subscribing to a nonexistent subject is valid in NATS), but it means import failures are invisible to clients. Server-side monitoring is the only way to detect them.

What’s the difference between a stream import and a service import?

A stream import subscribes to messages published on the exported subject — it’s a pub/sub pattern across account boundaries. A service import forwards request-reply messages — the importing account can send requests to a subject that’s handled by the exporting account’s service. Both types can become inactive for the same reasons (missing token, expired token, etc.). The impact differs: an inactive stream import means missing data; an inactive service import means service calls time out with no response.

Do I need activation tokens for every import?

Only for private exports. By default, service exports are private (require activation tokens) and stream exports are public (no tokens needed). You can explicitly set any export as public or private. For public exports, any account can import without an activation token. For private exports, each importing account needs a token generated by the exporting account.

How do I handle activation token expiration at scale?

Track token expiration dates as part of your credential management process. Use nsc to inspect tokens and identify upcoming expirations. In large deployments, automate token renewal — generate new tokens before expiration and push updated account JWTs. Some teams set long expiration windows (1-2 years) to reduce operational overhead, while others use no expiration and rely on key rotation for access revocation.

Can I fix an inactive import without restarting the server?

Yes. NATS JWT updates are applied without server restarts. After fixing the account JWT (adding a token, renewing an expired token, updating an import), push the JWT with nsc push. The server picks up the new JWT through its resolver and activates the import. Clients don’t need to reconnect — the import activation happens server-side and takes effect for existing subscriptions.

Proactive monitoring for NATS inactive jwt import with Synadia Insights

With 100+ always-on audit Checks from the NATS experts, Insights helps you find and fix problems before they become costly incidents.
No alert rules to write. No dashboards to maintain.

Start a 14-day Insights trial
Cancel