alibaba/arthas · error · IllegalArgumentException
cursor 缺少 offset
Error message
cursor 缺少 offset
What it means
Thrown by decodeCursor() in the ViewFile MCP tool when the decoded cursor JSON has no 'offset' field or 'offset' is not a Number. The cursor must carry a numeric offset to know where to resume reading. Surfaced to the caller as 'cursor 解析失败: cursor 缺少 offset' (error 98).
Source
Thrown at core/src/main/java/com/taobao/arthas/core/mcp/tool/function/basic1000/ViewFileTool.java:151
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);
cursor.put("offset", offset);
String json = JsonParser.toJson(cursor);
return Base64.getUrlEncoder().withoutPadding().encodeToString(json.getBytes(StandardCharsets.UTF_8));View on GitHub (pinned to 21cf2e9ba5)
Solutions
- Use cursor tokens returned by previous viewfile responses.
- If building manually, include a numeric "offset", e.g. {"v":1,"path":"/var/log/app.log","offset":0}.
Example fix
// before
cursor = base64url({"v":1,"path":"/var/log/app.log"})
// 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 numeric offset
if (!(offset instanceof Number)) throw new IllegalArgumentException("offset required");
c.put("offset", ((Number) offset).longValue()); Type guard
static boolean cursorHasValidOffset(Map<String,Object> m) {
return m.get("offset") instanceof Number;
} Try / catch
null
Prevention
- Reuse cursor tokens returned by prior viewfile calls.
- When crafting cursors, always include a numeric offset.
When it happens
Trigger: Calling viewfile with a cursor whose JSON omits 'offset' (e.g. {"v":1,"path":"/x"}) or has offset as a non-numeric type.
Common situations: Manually built cursor missing the offset key; schema drift between cursor versions; client serialization dropping numeric fields.
Related errors
AI-assisted analysis of alibaba/arthas@21cf2e9ba5 (2026-08-14).
Data as JSON: /api/errors/d3ffecb8016024e3.
Report an issue: GitHub.