{"record":{"id":"ccf63a509d298ecc","repo":"alibaba/arthas","slug":"cursor-path","errorCode":null,"errorMessage":"cursor 缺少 path","messagePattern":"cursor 缺少 path","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"core/src/main/java/com/taobao/arthas/core/mcp/tool/function/basic1000/ViewFileTool.java","lineNumber":148,"sourceCode":"        private final String path;\n        private final long offset;\n\n        private CursorValue(String path, long offset) {\n            this.path = path;\n            this.offset = offset;\n        }\n    }\n\n    private CursorValue decodeCursor(String cursor) {\n        try {\n            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);","sourceCodeStart":130,"sourceCodeEnd":166,"githubUrl":"https://github.com/alibaba/arthas/blob/21cf2e9ba52b305290be7223b980ff504bb9cb5b/core/src/main/java/com/taobao/arthas/core/mcp/tool/function/basic1000/ViewFileTool.java#L130-L166","documentation":"Thrown by decodeCursor() in the ViewFile MCP tool when a cursor token is decoded (base64url -> JSON) but the resulting map has no 'path' field, or 'path' is not a non-empty String. The cursor encodes a path and offset to resume reading; a missing path makes the cursor unusable.","triggerScenarios":"Calling viewfile with a cursor whose decoded JSON omits 'path' (e.g. {\"v\":1,\"offset\":0}) or has path=\"\"/non-string. Ultimately surfaced as 'cursor 解析失败: cursor 缺少 path' (error 98) because decodeCursor wraps it.","commonSituations":"Hand-crafting or stale cursor token missing the path field; truncated/corrupted cursor; client bug constructing the cursor JSON.","solutions":["Use only cursor tokens produced by a previous successful viewfile call (they always include path).","If constructing a cursor manually, ensure JSON has a non-empty string \"path\" field, e.g. {\"v\":1,\"path\":\"/var/log/app.log\",\"offset\":0}."],"exampleFix":"// before\ncursor = base64url({\"v\":1,\"offset\":0})\n// after\ncursor = base64url({\"v\":1,\"path\":\"/var/log/app.log\",\"offset\":0})","handlingStrategy":"validation","validationCode":"// When building a cursor, always include a non-empty path\nMap<String,Object> c = new LinkedHashMap<>();\nc.put(\"v\", 1);\nc.put(\"path\", Objects.requireNonNull(path, \"path\"));\nif (path == null || path.trim().isEmpty()) throw new IllegalArgumentException(\"path required\");\nc.put(\"offset\", offset);","typeGuard":"static boolean cursorHasValidPath(Map<String,Object> m) {\n    Object p = m.get(\"path\");\n    return p instanceof String && !((String) p).trim().isEmpty();\n}","tryCatchPattern":"null","preventionTips":["Reuse cursor tokens returned by prior viewfile calls.","When crafting cursors, always include a non-empty string path."],"tags":["arthas","mcp","viewfile","cursor","input-validation"],"backgroundTag":null,"analyzedSha":"21cf2e9ba52b305290be7223b980ff504bb9cb5b","analyzedAt":"2026-08-14T00:57:07.243Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}