karatelabs/karate · error · TypeError

String.prototype.matchAll called with a non-global RegExp…

Error message

String.prototype.matchAll called with a non-global RegExp argument

What it means

This TypeError is thrown by String.prototype.matchAll when given a RegExp without the global ('g') flag. matchAll iterates all matches, which is only well-defined for global regexes; the spec explicitly forbids non-global patterns. Note plain strings are auto-coerced to a global regex, so this only fires for actual non-global RegExp objects.

Solutions

  1. Add the g flag: str.matchAll(/foo/g)
  2. Clone with the flag: new RegExp(r.source, r.flags + 'g') before matchAll
  3. Check rgx.global before calling matchAll and normalize if false

Example fix

// before
for (const m of s.matchAll(/foo/)) {} // TypeError
// after
for (const m of s.matchAll(/foo/g)) {}
Defensive patterns

Strategy: type-guard

Validate before calling

const toGlobal = (r) => r instanceof RegExp ? new RegExp(r.source, r.flags.includes('g') ? r.flags : r.flags + 'g') : r;

Type guard

const isGlobalRegex = (r) => r instanceof RegExp && r.global;

Try / catch

try { return [...s.matchAll(rgx)]; } catch (e) { if (e instanceof TypeError && rgx instanceof RegExp) return [...s.matchAll(new RegExp(rgx.source, rgx.flags + 'g'))]; throw e; }

Prevention

When it happens

Trigger: Calling str.matchAll(/foo/) where the regex lacks 'g'. Distinguish from match, which accepts non-global regexes. Passing a regex built programmatically with flags omitted ('' or 'i' instead of 'g').

Common situations: Reusing a test regex (from .test() usage, often non-global) with matchAll; constructing RegExp from user-supplied flags; switching from str.match to str.matchAll without changing flags.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of karatelabs/karate@a22eb90246 (2026-09-12). Data as JSON: /api/errors/6db81a40eccecc3b. Report an issue: GitHub.

Appendix: source

Thrown at karate-js/src/main/java/io/karatelabs/js/JsStringPrototype.java:588

    private Object match(Context context, Object[] args) {
        String s = thisString(context, "match");
        if (args.length == 0 || args[0] == null || args[0] == Terms.UNDEFINED) {
            return List.of("");
        }
        JsRegex regex = (args[0] instanceof JsRegex r) ? r : new JsRegex(argString(args, 0, context));
        return regex.match(s);
    }

    private Object matchAll(Context context, Object[] args) {
        String s = thisString(context, "matchAll");
        // Spec: if regexp is a RegExp object, it must have the global flag.
        // Otherwise we coerce to a global RegExp (string-source patterns are auto-g).
        final JsRegex regex;
        if (args.length == 0 || args[0] == null || args[0] == Terms.UNDEFINED) {
            regex = new JsRegex("", "g");
        } else if (args[0] instanceof JsRegex r) {
            if (!r.global) {
                throw JsErrorException.typeError("String.prototype.matchAll called with a non-global RegExp argument");
            }
            regex = r;
        } else {
            regex = new JsRegex(argString(args, 0, context), "g");
        }
        java.util.regex.Matcher matcher = regex.javaPattern.matcher(s);
        JsIterator iter = new JsIterator() {
            boolean fetched;
            boolean done;
            JsArray pending;
            // §22.2.6.9 @@matchAll clones the receiver's matching state: start
            // from its current lastIndex, keep position iterator-local (the
            // original regex is never mutated), honor sticky anchoring.
            final boolean unicode = regex.flags.indexOf('u') >= 0;
            int nextIndex = regex.currentLastIndex();

            private void fetch() {
                if (fetched || done) return;

View on GitHub (pinned to a22eb90246)