{"record":{"id":"76f52ac8f0bf1ffd","repo":"alibaba/arthas","slug":"cursor-offset-76f52a","errorCode":null,"errorMessage":"cursor offset 不允许为负数","messagePattern":"cursor offset 不允许为负数","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"core/src/main/java/com/taobao/arthas/core/mcp/tool/function/basic1000/ViewFileTool.java","lineNumber":155,"sourceCode":"    }\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);\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);","sourceCodeStart":137,"sourceCodeEnd":173,"githubUrl":"https://github.com/alibaba/arthas/blob/21cf2e9ba52b305290be7223b980ff504bb9cb5b/core/src/main/java/com/taobao/arthas/core/mcp/tool/function/basic1000/ViewFileTool.java#L137-L173","documentation":"Thrown by decodeCursor() in the ViewFile MCP tool when the cursor's offset field is a Number but is negative. Even when supplied via cursor, the resume offset must be >= 0. Surfaced to the caller as 'cursor 解析失败: cursor offset 不允许为负数' (error 98).","triggerScenarios":"Calling viewfile with a cursor whose decoded offset is < 0, e.g. {\"v\":1,\"path\":\"/x\",\"offset\":-100} encoded as base64url.","commonSituations":"Tampered or buggy cursor with a negative offset; arithmetic mistake when generating a cursor client-side.","solutions":["Use cursor tokens produced by prior viewfile calls (their offset is always >= 0).","If constructing manually, set offset to a non-negative value."],"exampleFix":"// before\ncursor = base64url({\"v\":1,\"path\":\"/var/log/app.log\",\"offset\":-100})\n// after\ncursor = base64url({\"v\":1,\"path\":\"/var/log/app.log\",\"offset\":0})","handlingStrategy":"validation","validationCode":"long off = ((Number) offset).longValue();\nif (off < 0) throw new IllegalArgumentException(\"offset must be >= 0\");","typeGuard":"static boolean cursorOffsetNonNegative(Map<String,Object> m) {\n    Object o = m.get(\"offset\");\n    return o instanceof Number && ((Number) o).longValue() >= 0;\n}","tryCatchPattern":"null","preventionTips":["Reuse cursor tokens returned by prior viewfile calls.","When crafting cursors, set offset to a non-negative value."],"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"}