nats-io/nats-server · error

could not fetch < >

Error message

could not fetch <%q>: %v

What it means

URLAccResolver.Fetch performs an HTTP GET against the resolver URL plus the account nkey. If the HTTP client's Get returns a transport-level error (DNS failure, connection refused, TLS error, timeout), the resolver returns this error with the redacted URL and underlying cause.

Solutions

  1. Verify the resolver URL is correct and the HTTP service is up (curl the URL + account key)
  2. Fix DNS/firewall/TLS issues indicated by the wrapped underlying error
  3. Check nats-server resolver config matches the actual endpoint

Example fix

// before
resolver: URL(http://jwt-service:9090/) // service down
// after
# ensure service is running and reachable
curl http://jwt-service:9090/AB25...KEY
resolver: URL(http://jwt-service:9090/)
Defensive patterns

Strategy: retry

Validate before calling

resp, err := http.Get(resolverURL + accountKey)
if err != nil {
    return fmt.Errorf("account server unreachable before fetch: %w", err)
}
resp.Body.Close()

Try / catch

jwt, err := fetchAccount(resolver, key)
if err != nil && strings.Contains(err.Error(), "could not fetch") {
    // retry with backoff; check endpoint health
}

Prevention

When it happens

Trigger: Calling Fetch (directly or via fetchAccount) when the resolver HTTP endpoint is down, unreachable, misconfigured (wrong host/port/scheme), or the network path is broken (firewall, DNS).

Common situations: Resolver URL points to a JWT downloader service that is not running; wrong 'resolver: URL(...)' in server config; TLS certificate issues; network partition between nats-server and the account JWT server.

Understand the failure class

Background: 'Something went wrong' / 'Request failed (500)' / 'HTTP error! status: 404' — what failed HTTP requests actually mean and how to find the real cause — this error's family across 28 libraries.

Related errors


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

Appendix: source

Thrown at server/accounts.go:4249

	// We create our own transport to amortize TLS.
	tr := &http.Transport{
		MaxIdleConns:    10,
		IdleConnTimeout: 30 * time.Second,
	}
	ur := &URLAccResolver{
		url: url,
		c:   &http.Client{Timeout: DEFAULT_ACCOUNT_FETCH_TIMEOUT, Transport: tr},
	}
	return ur, nil
}

// Fetch will fetch the account jwt claims from the base url, appending the
// account name onto the end.
func (ur *URLAccResolver) Fetch(name string) (string, error) {
	url := ur.url + name
	resp, err := ur.c.Get(url)
	if err != nil {
		return _EMPTY_, fmt.Errorf("could not fetch <%q>: %v", redactURLString(url), err)
	} else if resp == nil {
		return _EMPTY_, fmt.Errorf("could not fetch <%q>: no response", redactURLString(url))
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		return _EMPTY_, fmt.Errorf("could not fetch <%q>: %v", redactURLString(url), resp.Status)
	}
	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return _EMPTY_, err
	}
	return string(body), nil
}

// Resolver based on nats for synchronization and backing directory for storage.
type DirAccResolver struct {
	*DirJWTStore
	*Server

View on GitHub (pinned to 3a66a489d2)