{"record":{"id":"c0eb1da934fec5ea","repo":"Tencent/matrix","slug":"can-only-rewind-one-step","errorCode":null,"errorMessage":"Can only rewind one step!","messagePattern":"Can only rewind one step!","errorType":"exception","errorClass":"ProcStatUtil.ParseException","httpStatus":null,"severity":"error","filePath":"matrix/matrix-android/matrix-battery-canary/src/main/java/com/tencent/matrix/batterycanary/utils/ProcStatReader.java","lineNumber":119,"sourceCode":"    public boolean hasReachedEOF() {\n        return mBufferSize == -1;\n    }\n\n    private void next() {\n        if (!hasNext()) {\n            throw new NoSuchElementException();\n        }\n\n        mPosition++;\n        mPrev = mChar;\n        mChar = (char) mBuffer[mPosition];\n\n        mRewound = false;\n    }\n\n    private void rewind() {\n        if (mRewound) {\n            throw new ParseException(\"Can only rewind one step!\");\n        }\n\n        mPosition--;\n        mChar = mPrev;\n        mRewound = true;\n    }\n\n\n    public CharBuffer readWord(CharBuffer buffer) {\n        buffer.clear();\n\n        boolean isFirstRun = true;\n\n        while (hasNext()) {\n            next();\n            if (!Character.isWhitespace(mChar)) {\n                if (!buffer.hasRemaining()) {\n                    CharBuffer newBuffer = CharBuffer.allocate(buffer.capacity() * 2);","sourceCodeStart":101,"sourceCodeEnd":137,"githubUrl":"https://github.com/Tencent/matrix/blob/3b8293bd65d47eeea7caf1f32a3a5d4d5eab60e7/matrix/matrix-android/matrix-battery-canary/src/main/java/com/tencent/matrix/batterycanary/utils/ProcStatReader.java#L101-L137","documentation":"ProcStatReader.rewind() steps the lexer back exactly one character; it guards with mRewound so two rewinds cannot happen without an intervening read. Throwing 'Can only rewind one step!' means the internal lexer invariant was violated - a second rewind was requested while one was already pending.","triggerScenarios":"A token-reading helper (readWord/readNumber/readToSymbol/skipPast) hits a non-matching character on the very first character it consumes and then again on a subsequent first-character mismatch, causing rewind() to be invoked twice without advancing.","commonSituations":"Malformed or unexpected /proc stat content where consecutive delimiters (e.g. a comm field not wrapped in parentheses, or two adjacent spaces) break the assumed token layout; usually indicates a kernel format the parser did not anticipate.","solutions":["Check the /proc/<pid>/stat content on the failing device for unexpected formatting (e.g. unusual process names) and report/adjust the parsing assumptions.","Catch ParseException and fall back to ProcStatUtil.parseWithBuffer, which parses from a raw byte buffer.","Ensure field-extraction helpers consume at least one valid character before rewinding; avoid calling skipPast/readWord on adjacent delimiters.","Update the Matrix version - newer releases harden proc stat parsing for edge-case formats."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try { CharBuffer w = reader.readWord(); } catch (ProcStatUtil.ParseException e) { reader.close(); reader = new ProcStatReader(path); }","preventionTips":["Use a fresh ProcStatReader per parse instead of reusing after failures","Never call two token readers back-to-back that can both fail on the first character","Prefer parseWithBuffer for untrusted stat formats","Report offending stat content to library maintainers"],"tags":["android","proc-stat","parser","internal-state"],"backgroundTag":"internal-invariant-violation","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"}