alibaba/arthas · error · IllegalArgumentException

cursor 缺少 path

Error message

cursor 缺少 path

What it means

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.

Source

Thrown at core/src/main/java/com/taobao/arthas/core/mcp/tool/function/basic1000/ViewFileTool.java:148

        private final String path;
        private final long offset;

        private CursorValue(String path, long offset) {
            this.path = path;
            this.offset = offset;
        }
    }

    private CursorValue decodeCursor(String cursor) {
        try {
            byte[] jsonBytes = Base64.getUrlDecoder().decode(cursor);
            String json = new String(jsonBytes, StandardCharsets.UTF_8);

            Map<String, Object> map = JsonParser.fromJson(json, new TypeReference<Map<String, Object>>() {});
            Object pathObj = map.get("path");
            Object offsetObj = map.get("offset");
            if (!(pathObj instanceof String) || ((String) pathObj).trim().isEmpty()) {
                throw new IllegalArgumentException("cursor 缺少 path");
            }
            if (!(offsetObj instanceof Number)) {
                throw new IllegalArgumentException("cursor 缺少 offset");
            }
            long offset = ((Number) offsetObj).longValue();
            if (offset < 0) {
                throw new IllegalArgumentException("cursor offset 不允许为负数");
            }
            return new CursorValue(((String) pathObj).trim(), offset);
        } catch (IllegalArgumentException e) {
            throw new IllegalArgumentException("cursor 解析失败: " + e.getMessage(), e);
        }
    }

    private String encodeCursor(String path, long offset) {
        Map<String, Object> cursor = new LinkedHashMap<>();
        cursor.put("v", 1);
        cursor.put("path", path);

View on GitHub (pinned to 21cf2e9ba5)

Solutions

  1. Use only cursor tokens produced by a previous successful viewfile call (they always include path).
  2. 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}.

Example fix

// before
cursor = base64url({"v":1,"offset":0})
// after
cursor = base64url({"v":1,"path":"/var/log/app.log","offset":0})
Defensive patterns

Strategy: validation

Validate before calling

// When building a cursor, always include a non-empty path
Map<String,Object> c = new LinkedHashMap<>();
c.put("v", 1);
c.put("path", Objects.requireNonNull(path, "path"));
if (path == null || path.trim().isEmpty()) throw new IllegalArgumentException("path required");
c.put("offset", offset);

Type guard

static boolean cursorHasValidPath(Map<String,Object> m) {
    Object p = m.get("path");
    return p instanceof String && !((String) p).trim().isEmpty();
}

Try / catch

null

Prevention

When it happens

Trigger: 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.

Common situations: Hand-crafting or stale cursor token missing the path field; truncated/corrupted cursor; client bug constructing the cursor JSON.

Related errors


AI-assisted analysis of alibaba/arthas@21cf2e9ba5 (2026-08-14). Data as JSON: /api/errors/ccf63a509d298ecc. Report an issue: GitHub.