inancgumus/learngo · error

record %d: %v

Error message

record %d: %v

What it means

In v5's pipeline, logCount.Each runs count over every record from the upstream iterator and, if the iterator returned an error, wraps it as 'record %d: %v' using lc.n+1 — the 1-based record number at which iteration stopped (each() doesn't invoke yield when an error occurs, hence n+1). This gives pipeline consumers record-level context for upstream parse errors.

Source

Thrown at logparser/v5/pipe/logcount.go:31

// logCount counts the yielded records.
type logCount struct {
	Iterator
	n int
}

// Each yields to the inner iterator while counting the records.
// Reports the record number on an error.
func (lc *logCount) Each(yield func(Record) error) error {
	count := func(r Record) error {
		lc.n++
		return yield(r)
	}

	err := lc.Iterator.Each(count)

	if err != nil {
		// lc.n+1: iterator.each won't call yield on err
		return fmt.Errorf("record %d: %v", lc.n+1, err)
	}
	return nil
}

// count returns the last read record number.
func (lc *logCount) count() int {
	return lc.n
}

View on GitHub (pinned to 3c475a78e5)

Solutions

  1. Look at the embedded underlying error for the real cause and fix that record
  2. Log-and-skip bad records in the upstream stage instead of failing the whole pipeline
  3. Include the raw record content in the wrapped message for faster diagnosis

Example fix

// before
if err != nil {
    return fmt.Errorf("record %d: %v", lc.n+1, err)
}
// after
if err != nil {
    if errors.Is(err, ErrBadRecord) {
        log.Printf("skipping record %d: %v", lc.n+1, err)
        return nil // continue processing
    }
    return fmt.Errorf("record %d: %w", lc.n+1, err)
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Go has no exceptions; guard the pipeline result:
if err := count(ErrIter); err != nil {
    var recErr *RecordError
    if errors.As(err, &recErr) { /* recErr.N identifies the record */ }
}

Type guard

type RecordError struct { N int; Err error }
func (e *RecordError) Error() string { return fmt.Sprintf("record %d: %v", e.N, e.Err) }
func asRecordError(err error) (*RecordError, bool) {
    var re *RecordError
    ok := errors.As(err, &re)
    return re, ok
}

Try / catch

if err := pipeline.Run(); err != nil {
    if re, ok := asRecordError(err); ok {
        log.Printf("failed at record %d: %v", re.N, re.Err)
        return nil // or re.Err for the root cause
    }
    return err
}

Prevention

When it happens

Trigger: Any upstream error in the pipe (e.g. a malformed record rejected by an earlier stage) surfaces here as 'record N: <underlying error>' when the logCount stage's Each call returns non-nil.

Common situations: Corrupt line at position N in a large streamed log, an earlier parsing stage emitting an error mid-stream, or a reader/I/O error propagating through the pipeline.

Related errors


AI-assisted analysis of inancgumus/learngo@3c475a78e5 (2026-09-02). Data as JSON: /api/errors/4282c3bce25cc6de. Report an issue: GitHub.