Yes, it was an SDK bug. The generated accountingApi.js mishandled the file contents when building the request body, so the attachment record was created but the content never made it through. The verified workaround while it was broken: skip the SDK method for attachments and POST to the API endpoint directly with Axios or fetch, passing the file bytes yourself (users confirmed this worked). The actual fix shipped in xero-node v9.0.0, per the maintainer. So the clean fix is to update to v9.0.0 or newer; stay on the direct-API workaround only if you are pinned to an affected version.
Source: https://github.com/XeroAPI/xero-node/issues/679