crowdsecurity/crowdsec · error

api register ( )

Error message

api register (%s): %w

What it means

RegisterClient wraps any registration failure that did NOT come with an HTTP response object. This means the request never got a server answer: transport-level failure such as DNS failure, connection refused, timeout, or TLS handshake error. The base URL is included to point at the unreachable target.

Solutions

  1. Confirm the LAPI is listening: 'curl -k <api_url>/health' from the registering host
  2. Fix api_url host/port/scheme (http vs https, unix socket path) in the configuration
  3. If TLS is self-signed, configure the CA cert pool or the client cert as documented instead of raw skipping
  4. Check DNS resolution and firewall rules between the watcher and LAPI hosts

Example fix

// before
return nil, fmt.Errorf("api register (%s): %w", c.BaseURL, err)
// after (caller pre-check)
if err := pingLAPI(ctx, baseURL); err != nil {
    return nil, fmt.Errorf("LAPI unreachable at %s: %w", baseURL, err)
}
return nil, fmt.Errorf("api register (%s): %w", c.BaseURL, err)
Defensive patterns

Strategy: validation

Validate before calling

// verify LAPI reachability before RegisterClient
u, err := url.Parse(config.URL.String())
if err != nil || u.Host == "" {
    return fmt.Errorf("invalid LAPI url %q", config.URL)
}
conn, err := net.DialTimeout("tcp", u.Host, 5*time.Second)
if err != nil {
    return fmt.Errorf("LAPI %s unreachable: %w", u.Host, err)
}
conn.Close()

Type guard

func isTransportError(err error) bool {
    return !errors.Is(err, context.Canceled) &&
        (errors.Is(err, syscall.ECONNREFUSED) ||
         os.IsTimeout(err) ||
         strings.Contains(err.Error(), "connection refused") ||
         strings.Contains(err.Error(), "no such host"))
}

Try / catch

if err != nil {
    if isTransportError(err) {
        // retry with backoff; LAPI may be starting
        return retryWithBackoff(ctx, func() error { _, err := apiclient.RegisterClient(ctx, config, nil); return err })
    }
    return err
}

Prevention

When it happens

Trigger: c.Auth.RegisterWatcher returns err with resp == nil (or resp.Response == nil): the LAPI host is down, the URL is wrong, the port is closed, DNS fails, TLS cert is untrusted, or the context was canceled mid-request.

Common situations: lapi configured on wrong host/port; LAPI service not started; firewall blocking the connection; self-signed cert without proper CA setup (tls rejection); unix socket path wrong; network outage.

Understand the failure class

Background: ECONNREFUSED and "connection refused" / "could not connect to server" errors: what they mean and how to fix them — this error's family across 44 libraries.

Related errors


AI-assisted analysis of crowdsecurity/crowdsec@909b515798 (2026-09-06). Data as JSON: /api/errors/c1a8b32692499900. Report an issue: GitHub.

Appendix: source

Thrown at pkg/apiclient/client.go:296

	userAgent := config.UserAgent
	if userAgent == "" {
		userAgent = useragent.Default()
	}

	c := &ApiClient{client: client, BaseURL: baseURL, UserAgent: userAgent, URLPrefix: config.VersionPrefix}
	c.common.client = c
	c.Decisions = (*DecisionsService)(&c.common)
	c.Alerts = (*AlertsService)(&c.common)
	c.Auth = (*AuthService)(&c.common)

	resp, err := c.Auth.RegisterWatcher(ctx, models.WatcherRegistrationRequest{MachineID: &config.MachineID, Password: &config.Password, RegistrationToken: config.RegistrationToken})
	if err != nil {
		/*if we have http status, return it*/
		if resp != nil && resp.Response != nil {
			return nil, fmt.Errorf("api register (%s) http %s: %w", c.BaseURL, resp.Response.Status, err)
		}

		return nil, fmt.Errorf("api register (%s): %w", c.BaseURL, err)
	}

	return c, nil
}

func createTransport(url *url.URL) (*http.Transport, *url.URL) {
	urlString := url.String()

	// TCP transport
	if !strings.HasPrefix(urlString, "/") {
		return nil, url
	}

	// Unix transport
	url.Path = "/"
	url.Host = "unix"
	url.Scheme = "http"

View on GitHub (pinned to 909b515798)