projectdiscovery/nuclei · error

failed to read header line: %s

Error message

failed to read header line: %s

What it means

While looping over header lines, textproto ReadLine returned an error before the blank line that terminates the header block. In practice: EOF, meaning the raw request was truncated and never contains the empty line separating headers from body.

Source

Thrown at pkg/input/types/http.go:267

		// remainder of the request target unchanged.
		if scheme, rest, ok := strings.Cut(target, urlutil.SchemeSeparator); ok {
			target = strings.ToLower(scheme) + urlutil.SchemeSeparator + rest
		}
		urlx, err = urlutil.ParseAbsoluteURL(target, true)
	} else {
		urlx, err = urlutil.ParseRawRelativePath(target, true)
	}
	if err != nil {
		return nil, fmt.Errorf("failed to parse url: %s", err)
	}
	rr.URL = *urlx

	// parse headers
	rr.Request.Headers = mapsutil.NewOrderedMap[string, string]()
	for {
		line, err := protoReader.ReadLine()
		if err != nil {
			return nil, fmt.Errorf("failed to read header line: %s", err)
		}
		if line == "" {
			// end of headers next is body
			break
		}
		key, value, found := strings.Cut(line, ":")
		if !found || key == "" {
			return nil, fmt.Errorf("invalid header line: %s", line)
		}
		value = strings.TrimSpace(value)
		// Host carries the authority rather than request metadata, and callers
		// read it off the URL: retryablehttp derives the wire Host from there,
		// and keeping it in the header map would expose it to header fuzzing as
		// if it were an ordinary header.
		if strings.EqualFold(key, "Host") {
			// an absolute request target takes precedence over the Host header
			if rr.URL.Host == "" {
				rr.URL.Host = value

View on GitHub (pinned to 265b3a3dec)

Solutions

  1. Terminate the header block with one empty line before the body (CRLF CRLF in wire format)
  2. If there is no body, still end the raw request with the blank line after headers
  3. Re-export the request from Burp 'Copy as raw' including the empty line

Example fix

# before
raw: |
  GET / HTTP/1.1
  Host: example.com

# after
raw: |
  GET / HTTP/1.1
  Host: example.com

  
Defensive patterns

Strategy: validation

Validate before calling

normalized := strings.ReplaceAll(raw, "\r\n", "\n")
head := strings.SplitN(normalized, "\n\n", 2)
if len(head) != 2 {
    return fmt.Errorf("raw request missing blank line after headers")
}

Try / catch

On this error, append the missing blank line terminator to the raw request and re-parse once; if it still fails, drop the entry.

Prevention

When it happens

Trigger: Raw request whose header section is not terminated by an empty line: input ends right after the last header, or the blank line was lost in copy-paste/YAML indentation.

Common situations: Templates where the raw block scalar omits the trailing blank line; CRLF confusion — note the parser breaks on a completely empty line, so ensure the separator line is truly empty (no stray spaces); truncated files.

Related errors


AI-assisted analysis of projectdiscovery/nuclei@265b3a3dec (2026-08-15). Data as JSON: /api/errors/ac67f2cdf324f2be. Report an issue: GitHub.