Conversation
… error
Every batch tool promises a per-row envelope — `{ batch, total, ok,
failed, results }` — so the operator can see which ids went through. One
failing row broke that promise: the tool returned a single opaque error
and no rows.
Live repro: `b24_task_pause { taskId: [4193, 4191, 99999999] }` answered
`Действие над задачей не разрешено`, while both real tasks HAD in fact
been paused. The operator has no way to tell, and re-running is the
natural next move.
The cause is the SDK contract, not the portal. With
`returnAjaxResult: true`, `AbstractBatch._createBatchArrayResult` builds
the row array and `_addBatchErrorsIfAny` then copies every per-row error
onto the envelope; `Result.isSuccess` is `errors.size === 0`. So one bad
row out of fifty marks the envelope failed while the other forty-nine
rows sit fully populated in `getData()`. `batchV2` / `batchV3` checked
the envelope first and threw, discarding them.
They now hand back the rows whenever rows came back, and throw only when
nothing did — transport failure, rejected envelope, malformed response.
That is what their own JSDoc already promised ("per-row failures do NOT
throw — they land in the returned array"); the implementation just
didn't. Per-row reporting via `mapBatchRows` is unchanged.
Live re-check after the fix: `b24_task_start { taskId: [4193, 4191,
99999999] }` → `ok: 2, failed: 1`, with the bad id named in its row.
The existing unit test meant to cover this mocked an envelope shape the
SDK never produces — `isSuccess: true` next to a failed row — so the gap
was invisible. The new cases pin the real shape for both helpers, plus
the no-rows path that must still throw. The batch section of
`skills/manage-bx24-template-mcp/adding-tools.md` now warns against
treating the envelope as authoritative inside a tool.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
A batch call with one bad id returned a single opaque error and no rows, hiding the fact that the other ids had already been applied.
batchV2/batchV3were checking the batch envelope'sisSuccessfirst, and the SDK marks that envelope failed for per-row errors while filling the rows anyway. They now return the rows whenever rows came back — which is what their own JSDoc already promised.Type of change
Linked issue
Checklist
pnpm lintpassespnpm typecheckpassespnpm testpassesdocs/andskills/(the batch section ofskills/manage-bx24-template-mcp/adding-tools.mdnow warns against re-checking the envelope inside a tool)Screenshots / logs
Before, against a live portal — two real tasks plus one nonexistent id:
Both real tasks were paused by that call. Nothing in the response says so.
After:
Notes for reviewers
The mechanism, since it decides whether the fix is right rather than merely convenient:
AbstractBatch._createBatchArrayResult(SDK 1.3.0) builds the row array and then calls_addBatchErrorsIfAny, which copies each per-row error onto the envelopeResult.Result.isSuccessisthis._errors.size === 0. So underreturnAjaxResult: true— which both helpers pass — the envelope is failed whenever any row failed, with every row still populated ingetData(). Checking the envelope before reading rows therefore throws away exactly the partial-failure detail the batch tools are built to report, andisHaltOnError: falseguarantees the other calls really did execute.New contract: rows present → return them; no rows → throw. Transport throws are untouched, and per-row reporting through
mapBatchRowsis unchanged, so no tool needed editing.One thing worth flagging honestly: the existing test
does not throw for per-row failures (isHaltOnError: false semantics)mockedisSuccess: truealongside a failed row — a shape the SDK cannot produce. It passed throughout, which is why the bug survived. I left it in place (it still documents the intent) and added cases that pin the real shape, including the no-rows path that must keep throwing.This affects every batch tool — the seven lifecycle verbs, checklist complete / renew / delete, rate, dependency add / remove, elapsed-time delete — so it's worth a close look at whether you'd rather have the envelope error also surfaced somewhere in the summary (e.g. a top-level
envelopeErrorfield) rather than only per row. I kept the payload shape unchanged on the grounds that the row errors already carry the same text.