{"record":{"id":"d3049f115f8ec6e4","repo":"alibaba/arthas","slug":"cursor-e-getmessage","errorCode":null,"errorMessage":"cursor 解析失败: ${e.getMessage()}","messagePattern":"cursor 解析失败: (.+?)","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"core/src/main/java/com/taobao/arthas/core/mcp/tool/function/basic1000/ViewFileTool.java","lineNumber":159,"sourceCode":"            byte[] jsonBytes = Base64.getUrlDecoder().decode(cursor);\n            String json = new String(jsonBytes, StandardCharsets.UTF_8);\n\n            Map<String, Object> map = JsonParser.fromJson(json, new TypeReference<Map<String, Object>>() {});\n            Object pathObj = map.get(\"path\");\n            Object offsetObj = map.get(\"offset\");\n            if (!(pathObj instanceof String) || ((String) pathObj).trim().isEmpty()) {\n                throw new IllegalArgumentException(\"cursor 缺少 path\");\n            }\n            if (!(offsetObj instanceof Number)) {\n                throw new IllegalArgumentException(\"cursor 缺少 offset\");\n            }\n            long offset = ((Number) offsetObj).longValue();\n            if (offset < 0) {\n                throw new IllegalArgumentException(\"cursor offset 不允许为负数\");\n            }\n            return new CursorValue(((String) pathObj).trim(), offset);\n        } catch (IllegalArgumentException e) {\n            throw new IllegalArgumentException(\"cursor 解析失败: \" + e.getMessage(), e);\n        }\n    }\n\n    private String encodeCursor(String path, long offset) {\n        Map<String, Object> cursor = new LinkedHashMap<>();\n        cursor.put(\"v\", 1);\n        cursor.put(\"path\", path);\n        cursor.put(\"offset\", offset);\n        String json = JsonParser.toJson(cursor);\n        return Base64.getUrlEncoder().withoutPadding().encodeToString(json.getBytes(StandardCharsets.UTF_8));\n    }\n\n    private List<Path> loadAllowedRoots() {\n        String config = System.getenv(ALLOWED_DIRS_ENV);\n\n        List<Path> roots = new ArrayList<>();\n        if (config != null && !config.trim().isEmpty()) {\n            String[] parts = config.split(\",\");","sourceCodeStart":141,"sourceCodeEnd":177,"githubUrl":"https://github.com/alibaba/arthas/blob/21cf2e9ba52b305290be7223b980ff504bb9cb5b/core/src/main/java/com/taobao/arthas/core/mcp/tool/function/basic1000/ViewFileTool.java#L141-L177","documentation":"Thrown by decodeCursor() in the ViewFile MCP tool as the outer catch wrapping any inner IllegalArgumentException (the missing path/missing offset/negative offset cases, errors 95-97). It can also surface for a cursor that fails base64url decoding or JSON parsing, since the inner operations can throw. The message appends the specific inner reason.","triggerScenarios":"Calling viewfile with any malformed cursor: not valid base64url, not valid JSON, or valid JSON missing required fields / with a negative offset. Any IllegalArgumentException inside the try block is rethrown with prefix 'cursor 解析失败:'.","commonSituations":"Using a stale/corrupted/truncated cursor token; passing a non-cursor string into the cursor field; client-side encoding bug; manually edited cursor.","solutions":["Discard the bad cursor and call viewfile with an explicit `path` (+ offset) to start fresh, then use the newly returned cursor.","Ensure the cursor is the exact base64url string returned by a prior viewfile response, unmodified.","If you must build a cursor, encode JSON {\"v\":1,\"path\":<non-empty string>,\"offset\":<non-negative number>} with URL-safe base64, no padding."],"exampleFix":"// before\nviewfile(cursor=\"not-a-valid-cursor\")\n// after\nviewfile(path=\"/var/log/app.log\", offset=0)  // get a fresh cursor, then reuse it","handlingStrategy":"try-catch","validationCode":"// Validate a cursor before sending it: decode + check shape\nstatic boolean isValidCursor(String cursor) {\n    try {\n        String json = new String(Base64.getUrlDecoder().decode(cursor), StandardCharsets.UTF_8);\n        Map<String,Object> m = JsonParser.fromJson(json, new TypeReference<Map<String,Object>>(){});\n        return m.get(\"path\") instanceof String && !((String)m.get(\"path\")).trim().isEmpty()\n            && m.get(\"offset\") instanceof Number && ((Number)m.get(\"offset\")).longValue() >= 0;\n    } catch (Exception e) { return false; }\n}","typeGuard":"null","tryCatchPattern":"try {\n    viewfile(cursor);\n} catch (IllegalArgumentException e) {\n    if (e.getMessage().startsWith(\"cursor 解析失败\")) {\n        // discard the bad cursor, fall back to a fresh path-based call\n        viewfile(path, 0L);\n    } else throw e;\n}","preventionTips":["Treat cursors as opaque tokens returned by the server; do not hand-edit them.","On any cursor failure, restart with an explicit path call to obtain a fresh cursor.","If building cursors, validate base64url + JSON shape + required non-negative numeric offset before use."],"tags":["arthas","mcp","viewfile","cursor","input-validation","parse-error"],"backgroundTag":null,"analyzedSha":"21cf2e9ba52b305290be7223b980ff504bb9cb5b","analyzedAt":"2026-08-14T00:57:07.243Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}