{"record":{"id":"3acd87fe8488ad32","repo":"henrygd/beszel","slug":"scanner-error-w","errorCode":null,"errorMessage":"scanner error: %w","messagePattern":"scanner error: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"agent/gpu.go","lineNumber":184,"sourceCode":"\tif err := cmd.Start(); err != nil {\n\t\treturn err\n\t}\n\n\tscanner := bufio.NewScanner(stdout)\n\tif c.buf == nil {\n\t\tc.buf = make([]byte, 0, c.bufSize)\n\t}\n\tscanner.Buffer(c.buf, bufio.MaxScanTokenSize)\n\n\tfor scanner.Scan() {\n\t\thasValidData := c.parse(scanner.Bytes())\n\t\tif !hasValidData {\n\t\t\treturn errNoValidData\n\t\t}\n\t}\n\n\tif err := scanner.Err(); err != nil {\n\t\treturn fmt.Errorf(\"scanner error: %w\", err)\n\t}\n\treturn cmd.Wait()\n}\n\n// getJetsonParser returns a function to parse the output of tegrastats and update the GPUData map\nfunc (gm *GPUManager) getJetsonParser() func(output []byte) bool {\n\t// use closure to avoid recompiling the regex\n\tramPattern := regexp.MustCompile(`RAM (\\d+)/(\\d+)MB`)\n\tgr3dPattern := regexp.MustCompile(`GR3D_FREQ (\\d+)%`)\n\ttempPattern := regexp.MustCompile(`(?:tj|GPU)@(\\d+\\.?\\d*)C`)\n\t// Orin Nano / NX do not have GPU specific power monitor\n\t// TODO: Maybe use VDD_IN for Nano / NX and add a total system power chart\n\tpowerPattern := regexp.MustCompile(`(GPU_SOC|CPU_GPU_CV)\\s+(\\d+)mW|VDD_SYS_GPU\\s+(\\d+)/\\d+`)\n\n\t// jetson devices have only one gpu so we'll just initialize here\n\tgpuData := &system.GPUData{Name: \"GPU\"}\n\tgm.GpuDataMap[\"0\"] = gpuData\n","sourceCodeStart":166,"sourceCodeEnd":202,"githubUrl":"https://github.com/henrygd/beszel/blob/b38fb7dafa60812cc22e6a84ce313e94f1ce0a32/agent/gpu.go#L166-L202","documentation":"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).","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before: treat all scanner errors as fatal\nif err := scanner.Err(); err != nil {\n    return fmt.Errorf(\"scanner error: %w\", err)\n}\n// after: tolerate cancellation\nif err := scanner.Err(); err != nil && ctx.Err() == nil {\n    return fmt.Errorf(\"scanner error: %w\", err)\n}","handlingStrategy":"try-catch","validationCode":"// ensure the collector command and context are sane before scanning\ncmd := exec.CommandContext(ctx, bin, args...)\nstdout, err := cmd.StdoutPipe()\nif err != nil { return err }","typeGuard":null,"tryCatchPattern":"if err := collect(); err != nil {\n    if errors.Is(err, context.Canceled) || ctx.Err() != nil {\n        return nil // intentional shutdown\n    }\n    var scanErr *fmt.wrapError\n    if errors.As(err, &scanErr) && errors.Is(scanErr, bufio.ErrTooLong) {\n        // increase scanner buffer and retry\n    }\n    return err\n}","preventionTips":["Increase scanner buffer for verbose tools like tegrastats","Expect cancellations during shutdown and filter them out","Monitor for OOM kills of collector processes"],"tags":["gpu","scanner","io"],"backgroundTag":"stream-read-error","analyzedSha":"b38fb7dafa60812cc22e6a84ce313e94f1ce0a32","analyzedAt":"2026-08-31T15:10:10.149Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}