{"record":{"id":"676e6bcf3f351dda","repo":"Tencent/matrix","slug":"couldn-t-read-number-because-the-file-ended","errorCode":null,"errorMessage":"Couldn't read number because the file ended!","messagePattern":"Couldn't read number because the file ended!","errorType":"exception","errorClass":"ProcStatUtil.ParseException","httpStatus":null,"severity":"warning","filePath":"matrix/matrix-android/matrix-battery-canary/src/main/java/com/tencent/matrix/batterycanary/utils/ProcStatReader.java","lineNumber":186,"sourceCode":"            next();\n            if (Character.isDigit(mChar)) {\n                result = result * 10 + (mChar - '0');\n            } else if (isFirstRun) {\n                if (mChar == '-') {\n                    sign = -1;\n                } else {\n                    throw new ParseException(\"Couldn't read number!\");\n                }\n            } else {\n                rewind();\n                break;\n            }\n\n            isFirstRun = false;\n        }\n\n        if (isFirstRun) {\n            throw new ParseException(\"Couldn't read number because the file ended!\");\n        }\n\n        return sign * result;\n    }\n\n    public CharBuffer readToSymbol(char symbol, CharBuffer buffer) {\n        buffer.clear();\n        boolean isFirstRun = true;\n\n        while (hasNext()) {\n            next();\n            if (symbol != mChar) {\n                if (!buffer.hasRemaining()) {\n                    CharBuffer newBuffer = CharBuffer.allocate(buffer.capacity() * 2);\n                    buffer.flip();\n                    newBuffer.put(buffer);\n                    buffer = newBuffer;\n                }","sourceCodeStart":168,"sourceCodeEnd":204,"githubUrl":"https://github.com/Tencent/matrix/blob/3b8293bd65d47eeea7caf1f32a3a5d4d5eab60e7/matrix/matrix-android/matrix-battery-canary/src/main/java/com/tencent/matrix/batterycanary/utils/ProcStatReader.java#L168-L204","documentation":"ProcStatReader.readNumber() throws ParseException(\"Couldn't read number because the file ended!\") when EOF is hit before any digit is consumed (isFirstRun still true). The numeric field the parser requested does not exist in the stream.","triggerScenarios":"Calling jiffies()/readNumber at the end of /proc/<pid>/stat - the file was truncated or the process exited while reading, so utime/stime are absent.","commonSituations":"Race between checking the pid and reading its stat file; very short-lived threads terminating during battery sampling.","solutions":["Treat this as process-exit: catch ParseException, remove the pid from tracking, and stop sampling it.","Check /proc/<pid> existence immediately before parsing to narrow the race window.","Retry on the next tick with a freshly opened reader; do not reuse a reader positioned at EOF.","Log the pid and tick so persistent failures for one pid can be detected and dropped."],"exampleFix":"// before\nlong jiffy = ProcStatUtil.instance.jiffies(pid, true);\n// after\ntry {\n    long jiffy = ProcStatUtil.instance.jiffies(pid, true);\n} catch (ProcStatUtil.ParseException e) {\n    mWatchingPids.remove(pid); // process ended\n}","handlingStrategy":"retry","validationCode":"java.io.File f = new File(\"/proc/\" + pid + \"/stat\");\nif (!f.exists() || f.length() < 16) { /* skip: likely dead process */ }","typeGuard":null,"tryCatchPattern":"try { ... } catch (ProcStatUtil.ParseException e) { /* EOF: treat as process exit, remove pid */ }","preventionTips":["Check pid liveness immediately before reading","Do not reuse readers positioned at EOF","Retry next tick; drop pid after repeated EOF failures","Keep sampling loop exception-safe per pid"],"tags":["android","proc-stat","eof","jiffies"],"backgroundTag":"file-read-failed","analyzedSha":"3b8293bd65d47eeea7caf1f32a3a5d4d5eab60e7","analyzedAt":"2026-09-08T08:01:39.722Z","contentChangedAt":"2026-09-08T08:01:39.722Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}