henrygd/beszel · error
scanner error: %w
Error message
scanner error: %w
What it means
In collect, after streaming a collector command's output through a scanner, any non-EOF error from scanner.Err() is wrapped as 'scanner error: %w'. This means reading the collector's stdout failed mid-stream (I/O error on the pipe) rather than simply finding no valid data (that is errNoValidData).
Source
Thrown at agent/gpu.go:184
if err := cmd.Start(); err != nil {
return err
}
scanner := bufio.NewScanner(stdout)
if c.buf == nil {
c.buf = make([]byte, 0, c.bufSize)
}
scanner.Buffer(c.buf, bufio.MaxScanTokenSize)
for scanner.Scan() {
hasValidData := c.parse(scanner.Bytes())
if !hasValidData {
return errNoValidData
}
}
if err := scanner.Err(); err != nil {
return fmt.Errorf("scanner error: %w", err)
}
return cmd.Wait()
}
// getJetsonParser returns a function to parse the output of tegrastats and update the GPUData map
func (gm *GPUManager) getJetsonParser() func(output []byte) bool {
// use closure to avoid recompiling the regex
ramPattern := regexp.MustCompile(`RAM (\d+)/(\d+)MB`)
gr3dPattern := regexp.MustCompile(`GR3D_FREQ (\d+)%`)
tempPattern := regexp.MustCompile(`(?:tj|GPU)@(\d+\.?\d*)C`)
// Orin Nano / NX do not have GPU specific power monitor
// TODO: Maybe use VDD_IN for Nano / NX and add a total system power chart
powerPattern := regexp.MustCompile(`(GPU_SOC|CPU_GPU_CV)\s+(\d+)mW|VDD_SYS_GPU\s+(\d+)/\d+`)
// jetson devices have only one gpu so we'll just initialize here
gpuData := &system.GPUData{Name: "GPU"}
gm.GpuDataMap["0"] = gpuData
View on GitHub (pinned to b38fb7dafa)
Solutions
- Check the wrapped cause (%w) to distinguish bufio.ErrTooLong / killed process from pipe errors
- Increase the scanner buffer if lines are very long
- Ignore the error during intentional cancellation/shutdown of collectors
- Ensure the GPU tool binaries are stable and not being killed by OOM
Example fix
// before: treat all scanner errors as fatal
if err := scanner.Err(); err != nil {
return fmt.Errorf("scanner error: %w", err)
}
// after: tolerate cancellation
if err := scanner.Err(); err != nil && ctx.Err() == nil {
return fmt.Errorf("scanner error: %w", err)
} Defensive patterns
Strategy: try-catch
Validate before calling
// ensure the collector command and context are sane before scanning
cmd := exec.CommandContext(ctx, bin, args...)
stdout, err := cmd.StdoutPipe()
if err != nil { return err } Try / catch
if err := collect(); err != nil {
if errors.Is(err, context.Canceled) || ctx.Err() != nil {
return nil // intentional shutdown
}
var scanErr *fmt.wrapError
if errors.As(err, &scanErr) && errors.Is(scanErr, bufio.ErrTooLong) {
// increase scanner buffer and retry
}
return err
} Prevention
- Increase scanner buffer for verbose tools like tegrastats
- Expect cancellations during shutdown and filter them out
- Monitor for OOM kills of collector processes
When it happens
Trigger: The bufio.Scanner reading the collector command's stdout returns a read error — the command was killed (context canceled, OOM), the pipe broke, or a scanner token exceeded the buffer size limit.
Common situations: Agent shutdown or GPU_COLLECTOR change cancels the command's context mid-read; extremely long tegrastats/nvidia-smi lines exceeding the scanner buffer; system OOM killing the collector process.
Related errors
- no valid GPU data found
- no configured GPU collectors are available
- no GPU found - see https://beszel.dev/guide/gpu
- no sysfs values found
- failed to load %s: %w
AI-assisted analysis of henrygd/beszel@b38fb7dafa (2026-08-31).
Data as JSON: /api/errors/3acd87fe8488ad32.
Report an issue: GitHub.