apache/cassandra · critical · AssertionError

Invalid custom verb id + id + - we only allow custom ids…

Error message

Invalid custom verb id + id + - we only allow custom ids between 0 and MAX_CUSTOM_VERB_ID

What it means

For CUSTOM-kind verbs, ids must fall in [0, MAX_CUSTOM_VERB_ID] so they fit in the custom id space after idForCustomVerb() offsetting. Exceeding that bound is treated as a developer error and fails with AssertionError in the Verb constructor.

Solutions

  1. Use a custom id between 0 and MAX_CUSTOM_VERB_ID; idForCustomVerb() computes the wire id
  2. Reuse an existing unused custom id slot instead of inventing a large one
  3. Check Verb.MAX_CUSTOM_VERB_ID/CUSTOM_VERB_START constants when adding verbs

Example fix

// before
MY_VERB(CUSTOM, CUSTOM_VERB_START, ...)
// after
MY_VERB(CUSTOM, 1, ...) // small custom index; wire id derived via idForCustomVerb
Defensive patterns

Strategy: validation

Validate before calling

if (kind == Verb.Kind.CUSTOM && !(id >= 0 && id <= Verb.MAX_CUSTOM_VERB_ID)) throw new IllegalArgumentException("custom verb id out of range: " + id);

Prevention

When it happens

Trigger: Declaring Verb(CUSTOM, id, ...) with id > MAX_CUSTOM_VERB_ID, e.g. picking a raw wire id equal to a built-in verb's id instead of a small custom index.

Common situations: Adding custom verbs (e.g. for messaging-coordinator or test extensions) and choosing an id from the built-in range instead of the custom range.

Understand the failure class

Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.

Related errors


AI-assisted analysis of apache/cassandra@88fd0f6a0e (2026-09-10). Data as JSON: /api/errors/6bfb8c9530b0633f. Report an issue: GitHub.

Appendix: source

Thrown at src/java/org/apache/cassandra/net/Verb.java:489

    {
        this(NORMAL, id, priority, expiration, stage, serializer, handler, responseVerb);
    }

    Verb(Kind kind, int id, Priority priority, ToLongFunction<TimeUnit> expiration, Stage stage, Supplier<? extends IVersionedAsymmetricSerializer<?, ?>> serializer, Supplier<? extends IVerbHandler<?>> handler)
    {
        this(kind, id, priority, expiration, stage, serializer, handler, null);
    }

    Verb(Kind kind, int id, Priority priority, ToLongFunction<TimeUnit> expiration, Stage stage, Supplier<? extends IVersionedAsymmetricSerializer<?, ?>> serializer, Supplier<? extends IVerbHandler<?>> handler, Verb responseVerb)
    {
        this.stage = stage;
        if (id < 0)
            throw new IllegalArgumentException("Verb id must be non-negative, got " + id + " for verb " + name());

        if (kind == CUSTOM)
        {
            if (id > MAX_CUSTOM_VERB_ID)
                throw new AssertionError("Invalid custom verb id " + id + " - we only allow custom ids between 0 and " + MAX_CUSTOM_VERB_ID);
            this.id = idForCustomVerb(id);
        }
        else
        {
            if (id > CUSTOM_VERB_START - MAX_CUSTOM_VERB_ID)
                throw new AssertionError("Invalid verb id " + id + " - we only allow ids between 0 and " + (CUSTOM_VERB_START - MAX_CUSTOM_VERB_ID));
            this.id = id;
        }
        this.priority = priority;
        this.serializer = serializer;
        this.handler = handler;
        this.responseVerb = responseVerb;
        this.expiration = expiration;
        this.kind = kind;
        // this is a little hacky, but reduces the number of parameters up top
        this.isResponse = name().endsWith("_RSP") || handler == RESPONSE_HANDLER;
    }

View on GitHub (pinned to 88fd0f6a0e)