different-ai/openwork · error · InterpreterRuntimeError

String.matchAll requires a regular expression with the globa

Error message

String.matchAll requires a regular expression with the global (g) flag: write /${pattern.source}/${pattern.flags}g, or use String.match for a single match.

What it means

String.matchAll requires a global regex; the host JS spec also throws a TypeError for non-global patterns here. The sandbox validates pattern.global after converting the argument via toHostRegex and throws with a message showing the fixed literal (flags + g) and suggesting String.match for a single match. Note this check only fires for patterns that survived toHostRegex without already erroring; the result is materialized as an array, not an iterator.

Source

Thrown at packages/codemode/src/interpreter/runtime.ts:453

        result = value.replace(str(0), str(1))
        break
      }
      result = value.replaceAll(str(0), str(1))
      break
    }
    case "match": {
      const pattern = toHostRegex(args[0], name, node)
      const matched = value.match(pattern)
      if (matched === null) return null
      // A global match is a plain array of matched strings; a non-global match carries
      // index/groups own properties, so bypass the copying data checkpoint to keep them.
      if (pattern.global) return boundedData(matched, "String.match result")
      return matchToValue(matched)
    }
    case "matchAll": {
      const pattern = toHostRegex(args[0], name, node, "g")
      if (!pattern.global) {
        throw new InterpreterRuntimeError(
          `String.matchAll requires a regular expression with the global (g) flag: write /${pattern.source}/${pattern.flags}g, or use String.match for a single match.`,
          node,
        )
      }
      // Materialized as an array (not an iterator); each entry is a match array with
      // index/groups own properties. Match count is bounded by the subject length.
      return Array.from(value.matchAll(pattern), matchToValue)
    }
    case "search": {
      result = value.search(toHostRegex(args[0], name, node))
      break
    }
    case "repeat": {
      const count = num(0)
      if (!Number.isFinite(count) || count < 0)
        throw new InterpreterRuntimeError("String.repeat expects a finite non-negative count.", node)
      result = value.repeat(count)
      break

View on GitHub (pinned to 2b7df46e8a)

Solutions

  1. Add the g flag: str.matchAll(/ab/g)
  2. Include 'g' when constructing dynamically: new RegExp(source, "g")
  3. Use String.match(pattern) instead if only the first match is needed
  4. For string search values, wrap them with an explicit global regex

Example fix

// before
for (const m of text.matchAll(/(\w+)=(\w+)/)) { ... }
// after
for (const m of text.matchAll(/(\w+)=(\w+)/g)) { ... }
Defensive patterns

Strategy: type-guard

Validate before calling

if (!(re instanceof RegExp && re.global)) throw new TypeError("matchAll needs a /g regex")
const matches = text.matchAll(re)

Type guard

const isGlobalRegex = (v) => v instanceof RegExp && v.global

Try / catch

try { out = [...text.matchAll(re)] } catch (e) { if (String(e.message).includes("global (g) flag")) out = text.match(re) !== null ? [text.match(re)] : []; else throw e }

Prevention

When it happens

Trigger: Calling str.matchAll(/ab/) without g; passing a regex built via new RegExp(src) with no flags; reusing a non-global regex shared with String.match or String.search; passing a string argument that toHostRegex converted into a non-global regex.

Common situations: Porting host code that iterated matchAll with a non-global regex and relied on the TypeError; generating regexes from user config where flags are omitted; switching from match to matchAll without adding the flag.

Related errors


AI-assisted analysis of different-ai/openwork@2b7df46e8a (2026-09-01). Data as JSON: /api/errors/6bf94c1c1190a873. Report an issue: GitHub.