karatelabs/karate · error · RuntimeException

cache() second argument must be a function:

Error message

cache() second argument must be a function: 

What it means

The second argument to HttpRequest.cache(key, fn) must be a JavaCallable (a function/l callable). If the value passed is not invokable (string, number, map, null), the library throws this error appended with the cache key to identify the offending call site.

Solutions

  1. Wrap the value in a function: request.cache('key', () => value)
  2. If you already have the value and just want to seed the cache, invoke it lazily anyway or write to the cache store directly
  3. Check that the referenced function name exists and is not shadowed by a variable holding data
  4. If calling from Java, pass a lambda/JavaCallable, not a raw Object

Example fix

// before
request.cache("token", token); // token is a string, throws
// after
request.cache("token", () => token);
Defensive patterns

Strategy: type-guard

Validate before calling

if (typeof fn !== 'function') throw new Error('cache() second argument must be a function for key: ' + key);
request.cache(key, fn);

Type guard

function isCallable(v) { return v != null && (typeof v === 'function' || v instanceof Java.type('io.karatelabs.common.JavaCallable')); }

Try / catch

try {
  request.cache(key, maybeFn);
} catch (RuntimeException e) {
  if (e.getMessage().startsWith("cache() second argument must be a function")) {
    request.cache(key, function() { return maybeFn; }); // wrap the value lazily
  } else { throw e; }
}

Prevention

When it happens

Trigger: Calling cache('key', someValue) where someValue is a pre-computed value or a variable that is null/undefined instead of a lambda; passing a Java object that is not a JavaCallable; typos like cache('k', fnName) where fnName is undefined and coerced to a non-callable.

Common situations: Users confusing cache() semantics — expecting it to store an existing value directly — when it is a lazy get-or-compute API; dynamic scripts where a function reference was shadowed by data; cross-language bindings (JS vs Java) where lambdas do not convert to JavaCallable.

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 karatelabs/karate@a22eb90246 (2026-09-12). Data as JSON: /api/errors/148c0cbcdf014759. Report an issue: GitHub.

Appendix: source

Thrown at karate-core/src/main/java/io/karatelabs/http/HttpRequest.java:762

     * on this request, and returns the same value on subsequent calls. The
     * store dies with the request — never crosses request boundaries.
     * <p>
     * The two-argument form is the only supported shape. If the key is
     * missing, the second argument must be a function (otherwise a runtime
     * error is raised). A cached {@code null} or {@code undefined} return
     * value is still a cache hit and does not re-invoke the function.
     */
    private JavaCallable cache() {
        return (context, args) -> {
            if (args.length < 2) {
                throw new RuntimeException("cache() requires (key, fn)");
            }
            String key = args[0] + "";
            if (cacheStore != null && cacheStore.containsKey(key)) {
                return cacheStore.get(key);
            }
            if (!(args[1] instanceof JavaCallable fn)) {
                throw new RuntimeException("cache() second argument must be a function: " + key);
            }
            Object value = fn.call(context);
            if (cacheStore == null) {
                cacheStore = new HashMap<>();
            }
            cacheStore.put(key, value);
            return value;
        };
    }

    @Override
    public Object jsGet(String key) {
        switch (key) {
            case "method":
                return method;
            case "body":
                return getBodyConverted();
            case "bodyString":

View on GitHub (pinned to a22eb90246)