different-ai/openwork · error · InterpreterRuntimeError

${ref.name} received malformed URI data: ${error instanceof

Error message

${ref.name} received malformed URI data: ${error instanceof Error ? error.message : String(error)}

What it means

encodeURI/decodeURI/encodeURIComponent/decodeURIComponent are wrapped so malformed input surfaces as a sandbox URIError with a clear message. The host throws when decoding invalid escape sequences (e.g. a stray %) or encoding lone surrogates; the wrapper rethrows it tagged as URIError with the original message.

Source

Thrown at packages/codemode/src/stdlib/url.ts:61

])

export const uriArgument = (value: unknown, label: string): string => coerceToString(boundedData(value, label))

export const invokeUriFunction = (ref: UriFunction, args: Array<unknown>, node: AstNode): string => {
  const value = uriArgument(args[0], `${ref.name} input`)
  try {
    switch (ref.name) {
      case "encodeURI":
        return encodeURI(value)
      case "encodeURIComponent":
        return encodeURIComponent(value)
      case "decodeURI":
        return decodeURI(value)
      case "decodeURIComponent":
        return decodeURIComponent(value)
    }
  } catch (error) {
    throw new InterpreterRuntimeError(
      `${ref.name} received malformed URI data: ${error instanceof Error ? error.message : String(error)}`,
      node,
    ).as("URIError")
  }
}

export const urlArgument = (value: unknown, label: string): string =>
  value instanceof SandboxURL ? value.url.href : uriArgument(value, label)

export const invokeURLStatic = (name: string, args: Array<unknown>, node: AstNode): unknown => {
  if (!urlStatics.has(name)) throw new InterpreterRuntimeError(`URL.${name} is not available in CodeMode.`, node)
  if (args.length === 0) throw new InterpreterRuntimeError(`URL.${name} requires a URL argument.`, node).as("TypeError")
  const input = urlArgument(args[0], `URL.${name} input`)
  const base = args[1] === undefined ? undefined : urlArgument(args[1], `URL.${name} base`)
  try {
    const url = new URL(input, base)
    return name === "canParse" ? true : new SandboxURL(url)
  } catch {

View on GitHub (pinned to 2b7df46e8a)

Solutions

  1. Validate/escape % as %25 before decoding user input.
  2. Wrap decode in try/catch and fall back to the raw value on failure.
  3. Ensure encoded strings are not split in the middle of a %XX sequence (decode before slicing).
  4. Replace lone surrogates or use well-formed string handling before encodeURI.

Example fix

// before
const decoded = decodeURIComponent(raw) // raw may contain "%"
// after
const decoded = raw.includes("%") ? decodeURIComponent(raw.replace(/%(?![0-9A-Fa-f]{2})/g, "%25")) : raw
Defensive patterns

Strategy: try-catch

Validate before calling

const safeDecode = (s) => typeof s === "string" && !/%(?![0-9A-Fa-f]{2})/.test(s) ? decodeURIComponent(s) : s

Try / catch

try { out = decodeURIComponent(raw) } catch (e) { out = raw /* or log + fallback */ }

Prevention

When it happens

Trigger: `decodeURIComponent("%")` or `decodeURIComponent("%E0%A4%A")` (truncated/invalid percent-escape); `encodeURI("\uD800")` (lone surrogate).

Common situations: Decoding user-supplied or third-party query strings that contain raw % characters; splitting an encoded string mid-escape before decoding; handling binary data chopped at arbitrary byte boundaries.

Understand the failure class

Related errors


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