Recursive AI

Help lines for runtime errors

All oneecma docs

The = help: line of a runtime report names the fix. It reads live values from a traced replay and never runs script.

"Is not a function" on a missing method

When recv.name(...) fails because the receiver has no such member, the help is chosen in this order:

  1. The receiver is a Promise: `x` is a Promise; `await` it first.
  2. Idiom table (below): a name other languages or libraries use that JavaScript's built-ins do not have. The help gives the JavaScript form.
  3. A primitive of the other kind has the method (Number(s).toFixed).
  4. A property holding the thing being called (result.items.map).
  5. Edit distance: did you mean recv.name2? (function), from members the receiver really has.

A spelling near-miss never outranks the idiom table, and the edit-distance rule is strict for short names: a name of up to four characters may differ by one edit only (sum no longer suggests some, size no longer suggests slice); longer names allow max(2, length / 3) edits. A name that contains another counts as close only when the shorter has five or more characters or is at least two thirds of the longer (items in lineItems, but not sum in summary). Names that differ only by case, _ or - (createdAt, created_at) are the closest match of all.

Idiom table

ReceiverNameHelp
Arraysum, totala.reduce((a, b) => a + b, 0)
Arraymax, minMath.max(...a), Math.min(...a)
Arrayaverage, avg, meana.reduce((a, b) => a + b, 0) / a.length
Arraycontains, hasa.includes(value)
Arraysize, lenthe property a.length
Arraycounta.length, or a.filter(item => ...).length
Arrayfirst, lasta[0], a.at(-1)
ArrayisEmpty, emptya.length === 0
Arrayappend, adda.push(value)
Arrayremove, deletea.splice(index, 1), or a.filter(...) for a copy
Array, Stringlength() calledlength is a property: a.length
Map, Setsize() calledsize is a property
Stringcontainss.includes(text)
Stringlen, sizethe property s.length
Stringstrip, lstrip, rstriptrim(), trimStart(), trimEnd()
StringtoUpper, upper, toLower, lowertoUpperCase(), toLowerCase()
Stringformata template literal
plain Objectkeys, values, entries called as methodsObject.keys(o) and so on
plain ObjectitemsObject.entries(o)
plain Objecthas, hasKey, containskey in o, Object.hasOwn(o, key)
Map, Setmap, filter, reduce, some, every, find[...m].map(...)
Map, Setcontainsm.has(key)
Map, Setlength, count, lenthe property m.size

The entries apply only when the receiver does not have a member of that name (the length() and size() rows are the reverse: the member exists and is a number). An own property of the same name always wins.

Invalid Date

When Invalid time value is thrown on a Date made by new Date(x) in the traced run, and x was an undefined read from a property, the report labels the call (`new Date(e.createdAt)`: `e.createdAt` is undefined, so the Date is invalid) and gives the missing-property help for the object that was read (did you mean e.created_at?, with camelCase and snake_case counted as the same word). Otherwise the help is the guard if (Number.isNaN(d.getTime())).

NaN made from an undefined

In the traced run, arithmetic, unary +/-, ++/-- and calls to built-ins (Number(x), Math.round(x)) that are handed an undefined from a property read, a call or a find produce a NaN that remembers that undefined (the id of its origin record travels in the NaN's payload bits; nothing outside a traced replay sees it, and every NaN test treats it as NaN). Where a built-in rejects such a NaN (new Array(n): Invalid array length; BigInt(n): The number NaN cannot be converted to a BigInt because it is not an integer), the primary label says `x.count * 2` is NaN, computed from an undefined and the missing-property label and did-you-mean for the read follow, as for any other undefined. A Date made from such a NaN is explained like a Date made from the undefined itself. A NaN that no undefined produced (0 / 0) gets no origin, and nothing is reported for a NaN that never reaches an error.

Iterating the result of a call

for (const x of obj.items()), [...obj.items()] and const [a] = f() fail with V8's one wording for both causes: obj.items is not a function or its return value is not iterable. The report looks at the callee: when it is not a function it is explained as a failed call (did you mean, what it is), and when it is a function the result is blamed (`obj.items(...)` returned something that is not iterable) with the help to return an array or to loop over Object.keys/values/entries. Which operand shapes use this wording follows what Node prints (iter-* rows of testdata/errors/cases.txt): the callee text of a call or new in for-of and array spread (a comma expression is spelled (0 , f), calls in callee position lose their (...)), for await (obj.o(...) is not a function or its return value is not async iterable), a declaration initializer whose callee is a plain name (const [a] = f(); a member callee gives the value-kind wording), and yield* of anything but a name (yield* (intermediate value) is not iterable). A spread argument in a call keeps Spread syntax requires ...iterable[Symbol.iterator] to be a function.