crowdsecurity/crowdsec · error
tag is empty
Error message
tag is empty
What it means
parseTag reads alphanumeric bytes at the start of the message content as the TAG; if the first non-PRI/timestamp/hostname content character is not alphanumeric, no tag bytes are collected and it returns 'tag is empty'. It is thrown because the RFC3164 CONTENT field begins with TAG and the parser cannot produce a valid Tag value otherwise.
Source
Thrown at pkg/acquisition/modules/syslog/internal/parser/rfc3164/parse.go:149
return nil
}
//We do not enforce tag len as quite a lot of syslog client send tags with more than 32 chars
func (r *RFC3164) parseTag() error {
tag := []byte{}
tmpPid := []byte{}
pidEnd := false
hasPid := false
for r.position < r.len {
c := r.buf[r.position]
if !utils.IsAlphaNumeric(c) {
break
}
tag = append(tag, c)
r.position++
}
if len(tag) == 0 {
return errors.New("tag is empty")
}
r.Tag = string(tag)
if r.position == r.len {
return nil
}
c := r.buf[r.position]
if c == '[' {
hasPid = true
r.position++
for r.position < r.len {
c = r.buf[r.position]
if c == ']' {
pidEnd = true
r.position++
break
}View on GitHub (pinned to 909b515798)
Solutions
- Verify the message follows RFC3164 CONTENT = TAG + optional '[pid]' + ':' + MSG; the first character after the hostname space must be alphanumeric.
- Prepend a tag at the source (rsyslog template with %programname%, syslog-ng ${PROGRAM}) for devices that do not emit one.
- Ensure exactly one space separates hostname and tag — an extra space makes parseHostname swallow the tag and leaves tag empty.
- If messages legitimately have no tag, route them through a parser path that tolerates missing tags or normalize them upstream before acquisition.
- Inspect the raw line: if the message ends after the hostname, the sender is dropping CONTENT — fix the emitter.
Example fix
// before: content starts with ':' — no TAG field
r.Parse([]byte("<34>Feb 3 09:12:01 host : connection lost")) // tag is empty
// after
r.Parse([]byte("<34>Feb 3 09:12:01 host app: connection lost")) Defensive patterns
Strategy: validation
Validate before calling
func contentStartsWithTag(msg string) bool {
// locate start of CONTENT: after '<PRI> timestamp hostname '
parts := strings.SplitN(msg, " ", 4)
return len(parts) == 4 && len(parts[3]) > 0 && isAlphaNumeric(parts[3][0])
}
func isAlphaNumeric(c byte) bool {
return c >= '0' && c <= '9' || c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z'
} Try / catch
if err := parser.Parse(line); err != nil {
if strings.Contains(err.Error(), "tag is empty") {
line = prependDefaultTag(line, "unknown") // rewrite or drop
}
} Prevention
- Configure senders to always emit a program/tag (rsyslog %programname%)
- Ensure exactly one space between hostname and TAG so parseHostname does not swallow it
- Add a ':' after the tag when the message body starts with punctuation
- Test the parser with raw samples from every emitting device before deploying
When it happens
Trigger: Calling Parse on a message whose content (after hostname) begins with a non-alphanumeric character: e.g. content starts with ':', ' ', '[' , '-' or punctuation instead of a tag like 'sshd'. Also when the message ends right after the hostname, leaving nothing to parse as tag.
Common situations: Messages missing the TAG field entirely (some devices send '<PRI><TS> host message' with no tag); a leading space anomaly making parseHostname consume the tag as hostname, leaving an empty tag; applications emitting free-form messages without a syslog tag; test messages like '<34>Feb 3 09:12:01 host : msg'.
Understand the failure class
Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.
Related errors
- pid inside tag must be a number
- timestamp is not valid
- pid inside tag must be closed with ']'
- message is empty
- unrecognized syslog message
AI-assisted analysis of crowdsecurity/crowdsec@909b515798 (2026-09-06).
Data as JSON: /api/errors/5645baf87e2f646a.
Report an issue: GitHub.