alibaba/COLA · error · ClassCastException

Value for key=[${key}] is not of type: [${type}], it is [(${

Error message

Value for key=[${key}] is not of type: [${type}], it is [(${value.getClass()})${value}]

What it means

ExecutionContext.get(key, type, defaultValue) throws ClassCastException when the stored value for the key exists but is not an instance of the requested type V. It is an eager, message-rich substitute for a lazy ClassCastException from type.cast.

Source

Thrown at cola-components/cola-component-job/src/main/java/com/alibaba/cola/job/ExecutionContext.java:145

    @Nullable
    public Object get(String key) {
        return this.extensions.get(key);
    }

    @Nullable
    public <V> V get(String key, Class<V> type) {
        Object value = this.extensions.get(key);
        return value == null ? null : this.get(key, type, null);
    }

    @Nullable
    public <V> V get(String key, Class<V> type, @Nullable V defaultValue) {
        Object value = this.extensions.get(key);
        if (value == null) {
            return defaultValue;
        } else if (!type.isInstance(value)) {
            throw new ClassCastException(
                    "Value for key=[" + key + "] is not of type: [" + type + "], it is [(" + value.getClass() + ")" + value
                            + "]");
        } else {
            return type.cast(value);
        }
    }

    public boolean containsKey(String key) {
        return this.extensions.containsKey(key);
    }

    @Nullable
    public Object remove(String key) {
        return this.extensions.remove(key);
    }

    public boolean containsValue(Object value) {
        return this.extensions.containsValue(value);

View on GitHub (pinned to 352e1a8675)

Solutions

  1. Align the Class<V> argument with the type actually put into the context
  2. Fix the writer side to store the expected type under that key
  3. Use a distinct key per type to avoid collisions between steps

Example fix

// before
String name = ctx.get("count", String.class, "0"); // count stored as Integer
// after
Integer count = ctx.get("count", Integer.class, 0);
Defensive patterns

Strategy: type-guard

Validate before calling

Object raw = executionContext.get(key);
if (raw != null && !type.isInstance(raw)) {
    throw new IllegalStateException("key '" + key + "' holds " + raw.getClass() + ", expected " + type);
}

Type guard

<V> V safeGet(ExecutionContext ctx, String key, Class<V> type, V def) {
    Object v = ctx.get(key);
    return type.isInstance(v) ? type.cast(v) : def;
}

Try / catch

try {
    value = executionContext.get(key, Foo.class, defaultFoo);
} catch (ClassCastException e) {
    log.warn("Type mismatch for key {} in execution context; using default", key, e);
    value = defaultFoo;
}

Prevention

When it happens

Trigger: Calling executionContext.get("someKey", Foo.class, def) while a value of an incompatible class (e.g. Integer vs String, or two different versions of the same class) was stored under that key via put/extension map.

Common situations: Writing a String and reading as Integer; job step A stores one type and step B reads another; classloader duplicates (same class name, different Class objects) in app-server deployments.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


AI-assisted analysis of alibaba/COLA@352e1a8675 (2026-09-08). Data as JSON: /api/errors/7e19c022db143c8a. Report an issue: GitHub.