You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
formatError never sees real Xero errors: xero-node v13 rejects with a JSON string, not an object #213
Every failed Xero API call is reported to the LLM as:
An unexpected error occurred while communicating with Xero.
Xero's actual explanation — validation messages, PostDataInvalidException details, everything — is discarded before it reaches the caller. This affects all tools, and it makes any write failure effectively undiagnosable from the client side.
Root cause
xero-node v13 issues requests through axios, which throws on any non-2xx response. The generated API methods catch that and re-reject with a string:
// node_modules/xero-node/dist/gen/api/accountingApi.jstry{constresponse=awaitaxios(localVarRequestOptions);
...
else{reject({response: response,body: body});}}catch(error){consterrorResponse=newApiError(error);reject(JSON.stringify(errorResponse.generateError()));// <-- a string}
formatError has branches for AxiosError, an object-shaped SDK error, and Error — but none for a string, so everything falls through to the catch-all at src/helpers/format-error.ts:78.
Any write with a value Xero rejects. Example — create-manual-journal currently sends NO_TAX, which Xero refuses (separate bug, see #182):
{ "ErrorNumber": 14, "Type": "PostDataInvalidException",
"Message": "JSON request body could not be read, Error converting value \"NO_TAX\" to type 'Xero.API.Library..LineAmountType'. Path 'ManualJournals[0].LineAmountTypes', line 1, position 399.." }
The tool reports only Error creating manual journal: An unexpected error occurred while communicating with Xero.
Why this is worth fixing carefully
The rejected string embeds response.request.headers.authorization — the caller's Bearer token. So the fix cannot simply return the string, or it reintroduces exactly the leak #173 closed. It needs to parse and then whitelist.
Impact
In one real session, eight consecutive write attempts (4 update-bank-transaction, 4 create-manual-journal) failed against live books. All eight returned the identical generic message. The model concluded the Xero API was down and gave up — when in fact two distinct, fixable bugs were being reported clearly by Xero and thrown away here.
Summary
Every failed Xero API call is reported to the LLM as:
Xero's actual explanation — validation messages,
PostDataInvalidExceptiondetails, everything — is discarded before it reaches the caller. This affects all tools, and it makes any write failure effectively undiagnosable from the client side.Root cause
xero-nodev13 issues requests through axios, which throws on any non-2xx response. The generated API methods catch that and re-reject with a string:formatErrorhas branches forAxiosError, an object-shaped SDK error, andError— but none for a string, so everything falls through to the catch-all atsrc/helpers/format-error.ts:78.Two consequences worth calling out:
isXeroSdkErrorbranch (added in fix: prevent token leak in formatError for xero-node SDK errors #173) is unreachable for accounting calls. Because axios throws first, the SDK never reaches itsreject({response, body})path.error instanceof AxiosError, which also never fires for SDK calls. The intent is right; the attachment point can't be reached.Reproduction
Any write with a value Xero rejects. Example —
create-manual-journalcurrently sendsNO_TAX, which Xero refuses (separate bug, see #182):{ "ErrorNumber": 14, "Type": "PostDataInvalidException", "Message": "JSON request body could not be read, Error converting value \"NO_TAX\" to type 'Xero.API.Library..LineAmountType'. Path 'ManualJournals[0].LineAmountTypes', line 1, position 399.." }The tool reports only
Error creating manual journal: An unexpected error occurred while communicating with Xero.Why this is worth fixing carefully
The rejected string embeds
response.request.headers.authorization— the caller's Bearer token. So the fix cannot simply return the string, or it reintroduces exactly the leak #173 closed. It needs to parse and then whitelist.Impact
In one real session, eight consecutive write attempts (4
update-bank-transaction, 4create-manual-journal) failed against live books. All eight returned the identical generic message. The model concluded the Xero API was down and gave up — when in fact two distinct, fixable bugs were being reported clearly by Xero and thrown away here.PR follows.