{"record":{"id":"1af33ef850896007","repo":"Tencent/matrix","slug":"couldn-t-read-string-because-file-ended","errorCode":null,"errorMessage":"Couldn't read string because file ended!","messagePattern":"Couldn't read string because 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":155,"sourceCode":"                    CharBuffer newBuffer = CharBuffer.allocate(buffer.capacity() * 2);\n                    buffer.flip();\n                    newBuffer.put(buffer);\n                    buffer = newBuffer;\n                }\n\n                buffer.put(mChar);\n            } else if (isFirstRun) {\n                throw new ParseException(\"Couldn't read string!\");\n            } else {\n                rewind();\n                break;\n            }\n\n            isFirstRun = false;\n        }\n\n        if (isFirstRun) {\n            throw new ParseException(\"Couldn't read string because file ended!\");\n        }\n\n        buffer.flip();\n        return buffer;\n    }\n\n    public long readNumber() {\n        long sign = 1;\n        long result = 0;\n        boolean isFirstRun = true;\n\n        while (hasNext()) {\n            next();\n            if (Character.isDigit(mChar)) {\n                result = result * 10 + (mChar - '0');\n            } else if (isFirstRun) {\n                if (mChar == '-') {\n                    sign = -1;","sourceCodeStart":137,"sourceCodeEnd":173,"githubUrl":"https://github.com/Tencent/matrix/blob/3b8293bd65d47eeea7caf1f32a3a5d4d5eab60e7/matrix/matrix-android/matrix-battery-canary/src/main/java/com/tencent/matrix/batterycanary/utils/ProcStatReader.java#L137-L173","documentation":"ProcStatReader.readWord() throws ParseException(\"Couldn't read string because file ended!\") when the loop finished without consuming any character (isFirstRun still true), i.e. EOF was reached before any word character. It means the stat stream ended earlier than the parser expected.","triggerScenarios":"Calling state()/readWord at (or after) the end of /proc/<pid>/stat - typically when the process died mid-read, truncating the file, or when the reader was already exhausted by previous field reads.","commonSituations":"Process termination while the battery canary walks the stat fields; a truncated or empty stat file on some devices/emulators.","solutions":["Check the process is alive before reading, or treat ParseException at EOF as 'process exited' and stop sampling that pid.","Catch ParseException and skip the tick; re-open the file on the next sample.","Reduce time between process enumeration and stat read to shrink the race window.","Validate the stat file is non-empty before parsing (file length > 0)."],"exampleFix":"// before\nStatModel stat = ProcStatUtil.instance.parse(pid);\n// after\ntry {\n    StatModel stat = ProcStatUtil.instance.parse(pid);\n} catch (ProcStatUtil.ParseException e) {\n    Log.w(TAG, \"stat read failed (process likely exited): \" + e.getMessage());\n}","handlingStrategy":"retry","validationCode":"java.io.File f = new File(\"/proc/\" + pid + \"/stat\");\nboolean readable = f.exists() && f.length() > 0;","typeGuard":null,"tryCatchPattern":"try { ... } catch (ProcStatUtil.ParseException e) { if (e.getMessage().contains(\"file ended\")) { /* mark pid dead */ } }","preventionTips":["Minimize delay between pid enumeration and stat read","Drop pids that repeatedly fail at EOF","Check /proc/<pid> existence before parsing","Keep a fresh reader per tick"],"tags":["android","proc-stat","eof","process-monitoring"],"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"}