{"record":{"id":"b2c10696f2a7c8e3","repo":"alibaba/arthas","slug":"path-cursor","errorCode":null,"errorMessage":"必须提供 path 或 cursor","messagePattern":"必须提供 path 或 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":120,"sourceCode":"    private static final class CursorRequest {\n        private final String path;\n        private final long offset;\n        private final boolean cursorUsed;\n\n        private CursorRequest(String path, long offset, boolean cursorUsed) {\n            this.path = path;\n            this.offset = offset;\n            this.cursorUsed = cursorUsed;\n        }\n    }\n\n    private CursorRequest parseCursorOrArgs(String path, String cursor, Long offset) {\n        if (cursor != null && !cursor.trim().isEmpty()) {\n            CursorValue decoded = decodeCursor(cursor.trim());\n            return new CursorRequest(decoded.path, decoded.offset, true);\n        }\n        if (path == null || path.trim().isEmpty()) {\n            throw new IllegalArgumentException(\"必须提供 path 或 cursor\");\n        }\n        if (offset != null && offset < 0) {\n            throw new IllegalArgumentException(\"offset 不允许为负数\");\n        }\n        long resolvedOffset = (offset != null) ? offset : 0L;\n        return new CursorRequest(path.trim(), resolvedOffset, false);\n    }\n\n    private static final class CursorValue {\n        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","sourceCodeStart":102,"sourceCodeEnd":138,"githubUrl":"https://github.com/alibaba/arthas/blob/21cf2e9ba52b305290be7223b980ff504bb9cb5b/core/src/main/java/com/taobao/arthas/core/mcp/tool/function/basic1000/ViewFileTool.java#L102-L138","documentation":"Thrown by parseCursorOrArgs() in the ViewFile MCP tool when neither a cursor nor a non-empty path argument is provided. The tool reads a file slice by absolute/relative path or resumes from an opaque cursor token; at least one must be supplied.","triggerScenarios":"Calling the viewfile tool with path=null/empty and cursor=null/empty. The cursor branch is only taken when cursor is non-blank, so both being empty reaches this guard.","commonSituations":"Calling viewfile with no arguments; passing path=\"\" by mistake; an MCP client omitting both fields in the request payload.","solutions":["Provide a non-empty `path` (absolute or relative to an allowed root), or","Provide a `cursor` token returned by a previous viewfile call to continue reading."],"exampleFix":"// before\nviewfile(path=null, cursor=null)\n// after\nviewfile(path=\"/var/log/app.log\", offset=0)","handlingStrategy":"validation","validationCode":"if ((cursor == null || cursor.trim().isEmpty())\n        && (path == null || path.trim().isEmpty())) {\n    throw new IllegalArgumentException(\"path or cursor required\");\n}","typeGuard":"static boolean hasViewFileTarget(String path, String cursor) {\n    return (path != null && !path.trim().isEmpty())\n        || (cursor != null && !cursor.trim().isEmpty());\n}","tryCatchPattern":"null","preventionTips":["Always pass either a path or a previously returned cursor.","Validate the request payload has at least one of the two fields."],"tags":["arthas","mcp","viewfile","input-validation","required-argument"],"backgroundTag":null,"analyzedSha":"21cf2e9ba52b305290be7223b980ff504bb9cb5b","analyzedAt":"2026-08-14T00:57:07.243Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}