Repository navigation
HF-131 (5/7): no error reports a cell that did not produce it - #1765
marcin-kordas-hoc wants to merge 17 commits into
Conversation
An error read out of a neighbouring cell is not produced by the cell that read it. This closes the remaining rootless-origin cases the choke-point redesign's propagated-skip needed but did not itself provide identities for: - A parsing error is attributed to 'parser' (ParsingErrorVertex.getCellValue). - An error value typed directly into a cell is attributed to 'user input' (CellContentParser's CellContent.Error constructor). - An error literal written into a formula (e.g. =#VALUE!+1) is attributed to 'literal' (FormulaParser's ErrorLiteral alternative). - A #SPILL! error reports the cell that failed to spill, not no address at all: setNoSpace() attaches itself as root, since it already has 'this' in hand. getScalarValue's range-to-scalar coercion class was measured, not assumed: the requesting cell is genuinely the one that made the invalid request, so 'no address' there is honest, not a bug, and is left unchanged. known-limitations.md gains one bullet: the six buildErrorWithRawInputAst sites in FormulaParser.ts (malformed references) stay unattributed rather than mislabeled, pending a future task. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
Task linked: HF-131 Verbose and actionable formula error messages |
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
hyperformula-docs | ae97e21 | Commit Preview URL Branch Preview URL |
Oct 23 2026, 07:18 AM |
Performance comparison of head (ae97e21) vs base (e745be2) |
|
Ran a closed-loop comparison of this stack's origin/address behavior against real Excel via One thing surfaced that isn't a bug, but is worth a product call: the CRUD-produced Should a CRUD-removed reference get an |
…rigin # Conflicts: # CHANGELOG.md
An error that describes a broken reference exists before any function sees it, so the enclosing call is only a reader. originNameForAstNode returns undefined for reference and error nodes, so such an error reached the enclosing FUNCTION_CALL unclaimed and non-propagated, and the postprocessing tail handed it to that call: =SUM(A2:A99999999999) reported SUM, =ABS(A2:A99999999999) reported ABS with an argument index that belonged to nothing, and =SUM(#REF!) after removing rows reported SUM. The identity is now attached where the error is built, which lets the existing first-wins rule block the reader by itself. A reference that cannot be resolved reports 'reference', whether the parser rejected it, the sheet is missing, the range spans several sheets, or the name is unknown; one destroyed by removing rows or columns reports 'removed reference'; the out-of-sheet error raised inside the OFFSET heuristic reports 'OFFSET', because OFFSET really did produce it. Errors that arise inside an array result are unchanged and still take the reading function's name; that needs either a hook over every ad-hoc array element or the function name threaded through three methods of the plugin base class, so it is left out and stated in the known limitations. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…that made it An error that is one element of a returned array never reached the scalar branch of evaluateAst's tail, so it arrived at the enclosing call unclaimed and that call took it: =INDEX(A1:A2+B1:B3, 3, 1) reported INDEX for an #N/A the + operator produced, and a per-element coercion failure carried an argumentIndex with no originFunction at all, which the public JSDoc promises never happens. The tail now stamps the elements of an array the node itself built, which is the same first-wins rule applied one level down. A range-backed value is skipped: it is a view over cells whose errors were already marked when they were read, and reading its data would materialise the whole range on the hot path. That guard is not cosmetic — without it 'Sheet A' measured +5.7% against a 0.6% base spread; with it, four alternating rounds against the branch point give -1.09% overall and nothing outside its own spread. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 2a81d2f. Configure here.
Bugbot caught a site the previous commit missed, in the same family. An array literal
whose rows do not line up builds a #REF! before any call sees it, and originNameForAstNode
had no case for that node, so the enclosing call took it: =SUM({1,2;3}) reported SUM and
=ABS({1,2;3}) reported ABS with an argument index that belonged to nothing.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Tobiadefami
left a comment
There was a problem hiding this comment.
The implementation looks good.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts: # CHANGELOG.md
…elog to released behaviour The originFunction JSDoc and the known-limitations entry now name every origin the engine produces, including `array literal`, and no longer describe two argumentIndex gaps that this change closes. The changelog drops the entries for origin behaviour that was never released, folds the origin names into the entry that adds the field, and keeps the `#SPILL!` address fix. The JSDoc of the scalar origin stamp sits above its own function again. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…hat spans sheets or a missing sheet `SUM`, `MAX` and the other aggregates read a range through their own path, which built the `#REF!` for a range spanning several sheets or pointing at a missing sheet without an origin, so the aggregate claimed it. Both errors now report `reference`, like the same errors built by the interpreter. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## feat/hf-131-origin-function #1765 +/- ##
===============================================================
+ Coverage 97.36% 97.38% +0.02%
===============================================================
Files 207 207
Lines 16327 16343 +16
Branches 3624 3630 +6
===============================================================
+ Hits 15896 15916 +20
+ Misses 423 419 -4
Partials 8 8
🚀 New features to boost your workflow:
|

Context
The PR stops a function or operator from claiming an error it only read, and gives
#SPILL!errors an address. It is part 5 of 7 of HF-131, stacked on #1764.=SUM(A2:A99999999999)reportedSUM.#SPILL!errors had no address.DependencyGraph.ts, the dependency transformers andNumericAggregationPlugin.tsset the origin where they build the error:reference,removed reference,literalorOFFSET.ParsingErrorVertex.tsandCellContentParser.ts:parseranduser input.Interpreter.ts: an array literal whose rows differ in length reportsarray literal. An error inside an array result names the operation that built the array.FormulaVertex.ts:setNoSpace()makes the formula vertex the root of its#SPILL!error.originFunctionandargumentIndex, anddocs/guide/known-limitations.md: the full list of origin names.=SUM(A2:A99999999999)SUMreference=DATE(2020,A99999999999,1)DATE,argumentIndex: 1reference, no index=SUM(Sheet1!A2:Sheet2!B2)SUMreference=SUM(A2)after row 2 is removedSUMremoved reference=SUM({1,2;3})SUMarray literal=INDEX(A1:A2+B1:B3,3,1), array arithmetic onINDEXaddWithEpsilon=1+parser#REF!typed into a celluser input=#REF!literal=SEQUENCE(2)blocked by a value belowSheet1!A1How did you test your changes?
fix/hf-131-rootless-origin, inunit/error-origin-function.spec.tsandunit/error-address-preservation.spec.ts.SUMorMAXreads (several sheets, a missing sheet) fail without theNumericAggregationPlugin.tschange, reportingSUM.Compatibility with Excel and Google Sheets
Not applicable: error types and displayed values do not change.
Public API changes
DetailedCellError.originFunction:reference,removed reference,parser,user input,literal,array literal.DetailedCellError.addressof a#SPILL!error is now the cell whose array could not spill, instead ofundefined.Out of scope and follow-ups
#SPILL!and#CYCLE!keeporiginFunctionundefined, because no function produced them. This is stated inknown-limitations.md.removed referenceis open for review: it follows the other lowercase names (parser,user input,literal).Types of changes
Related issues
Checklist
dev-docs/pages this change affects – or none needed it.