VictoriaMetrics/VictoriaMetrics · error

cannot find whitespace between metric and timestamp in %q

Error message

cannot find whitespace between metric and timestamp in %q

What it means

After stripping the 'put ' prefix, Row.unmarshal expects a whitespace separating the metric name from the timestamp. If no space is found, this error is returned with the offending string. The line is structurally invalid for the OpenTSDB put protocol.

Source

Thrown at lib/protoparser/opentsdb/parser.go:68

func (r *Row) reset() {
	r.Metric = ""
	r.Tags = nil
	r.Value = 0
	r.Timestamp = 0
}

func (r *Row) unmarshal(s string, tagsPool []Tag) ([]Tag, error) {
	r.reset()
	s = trimLeadingSpaces(s)
	if !strings.HasPrefix(s, "put ") {
		return tagsPool, fmt.Errorf("missing `put ` prefix in %q", s)
	}
	s = s[len("put "):]
	s = trimLeadingSpaces(s)
	n := strings.IndexByte(s, ' ')
	if n < 0 {
		return tagsPool, fmt.Errorf("cannot find whitespace between metric and timestamp in %q", s)
	}
	r.Metric = s[:n]
	if len(r.Metric) == 0 {
		return tagsPool, fmt.Errorf("metric cannot be empty")
	}
	tail := trimLeadingSpaces(s[n+1:])
	n = strings.IndexByte(tail, ' ')
	if n < 0 {
		return tagsPool, fmt.Errorf("cannot find whitespace between timestamp and value in %q", s)
	}
	timestamp, err := fastfloat.Parse(tail[:n])
	if err != nil {
		return tagsPool, fmt.Errorf("cannot parse timestamp from %q: %w", tail[:n], err)
	}
	r.Timestamp = int64(timestamp)
	tail = trimLeadingSpaces(tail[n+1:])
	valueStr := ""
	tagsStr := ""

View on GitHub (pinned to 5079fb58f1)

Solutions

  1. Format each line as 'put <metric> <timestamp> <value> <tags>' with plain ASCII spaces.
  2. Check for tab-separated output from the producing script and convert to spaces.
  3. Log/print the offending line (shown in the error) and fix its formatting.
  4. Validate lines with a regex before sending: ^put \S+ \d+ \S+.*$.

Example fix

// before
fmt.Printf("put %s\t%d\t%v\n", metric, ts, val)
// after
fmt.Printf("put %s %d %v\n", metric, ts, val)
Defensive patterns

Strategy: validation

Validate before calling

// ensure space-separated fields before sending
fields := strings.Fields(line)
if len(fields) < 4 {
    return fmt.Errorf("opentsdb line needs >=4 fields: %q", line)
}

Try / catch

if err := parser.Parse(body, cb); err != nil {
    if strings.Contains(err.Error(), "cannot find whitespace between metric and timestamp") {
        // offending line is quoted in the error
    }
}

Prevention

When it happens

Trigger: Sending a put line like 'put mymetric\t...' with a non-space separator that survives trimming, or 'put mymetric' with no fields at all.

Common situations: Using tabs or multiple-space formatting that leaves IndexByte(' ') failing after trimming; truncated lines from broken scripts or cut-and-paste; locale-related separators.

Related errors


AI-assisted analysis of VictoriaMetrics/VictoriaMetrics@5079fb58f1 (2026-09-03). Data as JSON: /api/errors/762db347995317e4. Report an issue: GitHub.