crowdsecurity/crowdsec · error

empty journalctl:// DSN

Error message

empty journalctl:// DSN

What it means

ConfigureByDSN accepts DSNs of the form journalctl://<query-string>. After stripping the prefix, an empty remainder means there are no parameters at all — in particular no filters — so the source would be unusable and the call fails in pkg/acquisition/modules/journalctl/config.go:91.

Source

Thrown at pkg/acquisition/modules/journalctl/config.go:91

	s.metricsLevel = metricsLevel
	return nil
}

func (s *Source) ConfigureByDSN(_ context.Context, dsn string, labels map[string]string, logger *log.Entry, uuid string) error {
	var (
		filters  []string
		since    string
		logLevel log.Level
	)

	// format for the DSN is : journalctl://filters=FILTER1&filters=FILTER2
	if !strings.HasPrefix(dsn, "journalctl://") {
		return fmt.Errorf("invalid DSN %s for journalctl source, must start with journalctl://", dsn)
	}

	qs := strings.TrimPrefix(dsn, "journalctl://")
	if qs == "" {
		return errors.New("empty journalctl:// DSN")
	}

	params, err := url.ParseQuery(qs)
	if err != nil {
		return fmt.Errorf("could not parse journalctl DSN: %w", err)
	}

	for key, value := range params {
		switch key {
		case "filters":
			filters = append(filters, value...)
		case "log_level":
			if len(value) != 1 {
				return errors.New("expected exactly one value for 'log_level'")
			}

			lvl, err := log.ParseLevel(value[0])
			if err != nil {

View on GitHub (pinned to 909b515798)

Solutions

  1. Append query parameters, at minimum filters: journalctl://?filters=_SYSTEMD_UNIT=ssh.service.
  2. If configuring via YAML instead, use an acquis.yaml entry with journal_filter rather than a bare DSN.
  3. Debug DSN generation code to ensure the query string is not dropped or truncated.

Example fix

// before
crowdsec -type syslog -dsn 'journalctl://'

// after
crowdsec -type syslog -dsn 'journalctl://?filters=_SYSTEMD_UNIT=ssh.service&log_level=info'
Defensive patterns

Strategy: validation

Validate before calling

qs := strings.TrimPrefix(dsn, "journalctl://")
if qs == "" {
    return errors.New("journalctl DSN needs at least a filters parameter")
}

Type guard

func validJournalctlDSN(dsn string) bool {
    q, ok := strings.CutPrefix(dsn, "journalctl://")
    return ok && q != ""
}

Try / catch

if err := src.ConfigureByDSN(ctx, dsn); err != nil {
    if strings.Contains(err.Error(), "empty journalctl:// DSN") {
        // append ?filters=... and retry
    }
}

Prevention

When it happens

Trigger: Calling ConfigureByDSN(ctx, "journalctl://") with no query string, or building the DSN from variables that are all empty.

Common situations: Template/CLI assembly that drops the query string; truncation of the DSN at the '?' by another tool; copy-paste that lost the parameters.

Understand the failure class

Background: "Invalid URL" errors: why new URL(), URI.parse, and reqwest::Url reject your string — missing scheme, whitespace, and bad path format — this error's family across 39 libraries.

Related errors


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