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
- Look at the embedded underlying error for the real cause and fix that record
- Log-and-skip bad records in the upstream stage instead of failing the whole pipeline
- 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
- Wrap with %w instead of %v so errors.Is/As can unwrap the cause
- Skip-and-log recoverable record errors instead of failing the pipeline
- Include the raw record data in the wrapped message
- Define sentinel errors (ErrBadRecord) to classify failures in the pipeline
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.