apache/skywalking · warning · IllegalArgumentException

invalid_limits

invalid_limits

Error message

Request body is not valid JSON: {re.getMessage()}. Send an empty body for defaults, or a valid JSON object with optional `recordCap`, `retentionMillis`, `granularity` fields.

What it means

DSLDebuggingRestHandler.parseLimits() accepts either an empty body (defaults) or a JSON object with optional recordCap, retentionMillis, granularity fields when creating a debugging session. Gson parsing/conversion of the body throws a RuntimeException (JsonSyntaxException or IllegalStateException from getAsJsonObject) which is converted into this IllegalArgumentException, surfaced to the REST caller as an invalid_limits error.

Source

Thrown at oap-server/server-admin/dsl-debugging/src/main/java/org/apache/skywalking/oap/server/admin/dsl/debugging/rest/DSLDebuggingRestHandler.java:449

    }

    /**
     * Parses the optional JSON body. Throws {@link IllegalArgumentException}
     * with a user-facing message on malformed JSON or out-of-range limits,
     * which the caller maps to {@code 400 invalid_limits}. An empty body
     * stays mapped to {@link SessionLimits#DEFAULT} so a plain
     * {@code POST /dsl-debugging/session?...} without a body just works.
     */
    private SessionLimits parseLimits(final HttpData body) {
        if (body == null || body.length() == 0) {
            return SessionLimits.DEFAULT;
        }
        final JsonObject root;
        try {
            root = JsonParser.parseString(
                new String(body.array(), StandardCharsets.UTF_8)).getAsJsonObject();
        } catch (final RuntimeException re) {
            throw new IllegalArgumentException(
                "Request body is not valid JSON: " + re.getMessage()
                    + ". Send an empty body for defaults, or a valid JSON object "
                    + "with optional `recordCap`, `retentionMillis`, `granularity` "
                    + "fields.");
        }
        final int recordCap = root.has("recordCap")
            ? root.get("recordCap").getAsInt() : SessionLimits.DEFAULT.getRecordCap();
        final long retention = root.has("retentionMillis")
            ? root.get("retentionMillis").getAsLong()
            : SessionLimits.DEFAULT.getRetentionMillis();
        final Granularity granularity = root.has("granularity")
            ? Granularity.ofWireName(root.get("granularity").getAsString())
            : SessionLimits.DEFAULT.getGranularity();
        // Let SessionLimits validate the bounds — caller surfaces 400 invalid_limits.
        return new SessionLimits(recordCap, retention, granularity);
    }

    private static JsonObject ruleKeyToJson(final RuleKey key) {

View on GitHub (pinned to 102af09b4a)

Solutions

  1. Send no body at all to accept defaults, or a well-formed JSON object: {"recordCap": 100, "retentionMillis": 300000, "granularity": "..."}
  2. Validate the payload with a JSON linter or jq before sending; pass it via a file: curl --data-binary @limits.json
  3. Ensure the client sends raw JSON (Content-Type: application/json) and not form-encoding

Example fix

# before
curl -X POST 'http://oap:8092/dsl-debugging/session?...' -d 'recordCap=100'
# after
curl -X POST 'http://oap:8092/dsl-debugging/session?...' \
  -H 'Content-Type: application/json' \
  -d '{"recordCap":100,"retentionMillis":300000}'
Defensive patterns

Strategy: validation

Validate before calling

// Client-side: build the body with a JSON library, never string concat
ObjectNode n = JsonNodeFactory.instance.objectNode();
if (recordCap > 0) n.put("recordCap", recordCap);
if (retentionMillis > 0) n.put("retentionMillis", retentionMillis);
String body = n.isEmpty() ? "" : n.toString();

Type guard

function isLimitsBody(v: unknown): v is { recordCap?: number; retentionMillis?: number; granularity?: string } {
  return v === undefined || (typeof v === 'object' && v !== null && !Array.isArray(v));
}

Try / catch

REST clients should treat the 4xx invalid_limits response as terminal client error: surface the message (it embeds the Gson parse failure and the accepted fields) and do not retry with the same body.

Prevention

When it happens

Trigger: POST /dsl-debugging/session with a body that is not valid JSON (truncated JSON, stray quotes, form-encoded payload), or valid JSON that is not an object (array, bare string/number); sending Content-Type JSON with an empty-but-present body containing whitespace also fails parsing.

Common situations: Calling the debugging REST API with curl and misquoted payloads; proxies or clients that mangle the body; automated scripts generating JSON via string concatenation with unescaped characters; sending a JSON array of limit objects instead of one object.

Related errors


AI-assisted analysis of apache/skywalking@102af09b4a (2026-08-14). Data as JSON: /api/errors/4ad9720be061d2a6. Report an issue: GitHub.