{"record":{"id":"a2df8cff928051d9","repo":"prometheus/node_exporter","slug":"interrupts-empty","errorCode":null,"errorMessage":"interrupts empty","messagePattern":"interrupts empty","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"collector/interrupts_linux.go","lineNumber":83,"sourceCode":"\nfunc getInterrupts() (map[string]interrupt, error) {\n\tfile, err := os.Open(procFilePath(\"interrupts\"))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer file.Close()\n\n\treturn parseInterrupts(file)\n}\n\nfunc parseInterrupts(r io.Reader) (map[string]interrupt, error) {\n\tvar (\n\t\tinterrupts = map[string]interrupt{}\n\t\tscanner    = bufio.NewScanner(r)\n\t)\n\n\tif !scanner.Scan() {\n\t\treturn nil, errors.New(\"interrupts empty\")\n\t}\n\tcpuNum := len(strings.Fields(scanner.Text())) // one header per cpu\n\n\tfor scanner.Scan() {\n\t\t// On aarch64 there can be zero space between the name/label\n\t\t// and the values, so we need to split on `:` before using\n\t\t// strings.Fields() to split on fields.\n\t\tgroup := strings.SplitN(scanner.Text(), \":\", 2)\n\t\tif len(group) > 1 {\n\t\t\tparts := strings.Fields(group[1])\n\n\t\t\tif len(parts) < cpuNum+1 { // irq + one column per cpu + details,\n\t\t\t\tcontinue // we ignore ERR and MIS for now\n\t\t\t}\n\t\t\tintName := strings.TrimLeft(group[0], \" \")\n\t\t\tintr := interrupt{\n\t\t\t\tvalues: parts[0:cpuNum],\n\t\t\t}","sourceCodeStart":65,"sourceCodeEnd":101,"githubUrl":"https://github.com/prometheus/node_exporter/blob/17ddd77c59ba27e1508e9f7894b1e55b44d6aed3/collector/interrupts_linux.go#L65-L101","documentation":"parseInterrupts parses /proc/interrupts. The file must begin with a CPU header line (one column per CPU). If the first `scanner.Scan()` returns false, the file is empty and there is no header to count CPUs from, so the parser returns \"interrupts empty\". This typically means the kernel's interrupts proc file could not be read or is genuinely empty.","triggerScenarios":"getInterrupts opens /proc/interrupts and hands the reader to parseInterrupts; the very first scan hits EOF immediately — /proc/interrupts is zero bytes (e.g. permission/restricted /proc, unusual kernel config with CONFIG_GENERIC_IRQ not exposing it, or a corrupted/unsupported procfs in a VM/container). Also triggered by tests passing an empty reader.","commonSituations":"Running inside containers or hardened VMs where /proc is masked or minimal (e.g. some hypervisors, gVisor, LXC with lxcfs); hardened seccomp/AppArmor setups hiding proc entries; broken /proc mounts; unit tests feeding empty fixtures.","solutions":["Check the file on the host: `wc -c /proc/interrupts` and `head -1 /proc/interrupts`; if truly empty, the kernel/procfs is not providing it.","Disable the interrupts collector with `--collector.interrupts` omitted / use `--collector.disable-defaults` and opt into what works.","Fix the environment: correct /proc mount or container runtime masking (lxcfs, gVisor, seccomp filters) so /proc/interrupts is visible.","Upgrade the kernel if the platform genuinely doesn't expose interrupts via procfs; nothing the exporter can parse otherwise."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Verify /proc/interrupts is readable and non-empty before enabling the collector\nimport \"os\"\n\nfunc interruptsProcUsable() error {\n    f, err := os.Open(\"/proc/interrupts\")\n    if err != nil { return err }\n    defer f.Close()\n    buf := make([]byte, 1)\n    if _, err := f.Read(buf); err != nil {\n        return errors.New(\"/proc/interrupts is empty or unreadable\")\n    }\n    return nil\n}","typeGuard":null,"tryCatchPattern":"if _, err := getInterrupts(); err != nil {\n    if err.Error() == \"interrupts empty\" {\n        logger.Warn(\"interrupts collector unavailable: /proc/interrupts empty (sandboxed/containerized env?)\")\n        return\n    }\n    return err\n}","preventionTips":["Check `head -1 /proc/interrupts` in the target environment before deploying with the interrupts collector.","Avoid masked/minimal /proc mounts (lxcfs, gVisor) when this collector is enabled.","Use --collector.disable-defaults with explicit enables so an unusable collector doesn't mask others.","Alert on node_scrape_collector_success{collector=\"interrupts\"} == 0."],"tags":["linux","procfs","interrupts","monitoring"],"backgroundTag":"empty-required-field","analyzedSha":"17ddd77c59ba27e1508e9f7894b1e55b44d6aed3","analyzedAt":"2026-09-07T17:54:06.211Z","contentChangedAt":"2026-09-07T17:54:06.211Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}