Skip to content

fix(chatwoot-adapter): tell the operator why a relay failed - #66

Merged
rmyndharis merged 1 commit into
mainfrom
fix/chatwoot-adapter-relay-diagnostics
Aug 5, 2026
Merged

fix(chatwoot-adapter): tell the operator why a relay failed#66
rmyndharis merged 1 commit into
mainfrom
fix/chatwoot-adapter-relay-diagnostics

Conversation

@rmyndharis

Copy link
Copy Markdown
Owner

The problem

When an inbound message fails to reach Chatwoot, the adapter records the reason — the API status and response body, a refused private address, a certificate error — through deps.log, which reaches the host's stdout and nowhere else. The dashboard renders a plugin's healthCheck message, and that message carried only counts.

So the entire diagnostic an operator gets is:

1 dead-lettered after 5 attempts

That is enough to know something is wrong and not enough to do anything about it. It also takes about three minutes to appear, because the count only moves once the retry budget is exhausted. A wrong API token, a baseUrl typo, an unreachable self-hosted instance and a contact-identity collision all produce that same sentence.

See #63, where an operator spent days on a first-time setup with no way to see what was being rejected.

The changes

The health check now carries the last relay error. A failed relay records its message, and healthCheck appends last error: … alongside the counts. It is recorded on the first failure rather than at dead-letter time, so the reason is visible while the message is still pending retry instead of three minutes later. It is appended only when something is actually failing — a healthy plugin does not display a stale error — and truncated to 300 characters.

A baseUrl carrying a path is rejected when settings are saved. The client appends /api/v1/accounts/<id> to this value, so a URL copied out of the Chatwoot dashboard's address bar produces a request path like:

https://chat.example.com/app/accounts/2/settings/inboxes/8/api/v1/accounts/2/contacts/search

That 404s on every request, while the plugin enables cleanly and reports healthy until the retries run out. Config validation already rejected a non-https or credentialed baseUrl for the same reason; it just never looked at the path. It now has to be the origin.

Notes

onRelayError is a required member of InboundDeps, matching the existing onInboundLost and onBackfillExhausted callbacks. Making it optional would let a missed wiring pass silently, which is the failure mode this PR exists to remove.

An enable-time connectivity preflight was considered and deliberately left out. A preflight that throws would block enabling the plugin whenever Chatwoot is briefly unreachable, and the last-error surface already covers every failure class rather than only the ones a preflight would catch. If it is wanted later, the safe shape is a non-blocking probe whose result is reported through healthCheck.

This does not close #63 on its own — the reporter's actual cause is still unknown, and there are several candidates. It does mean the next operator in that position can read the answer off the dashboard instead of filing an issue.

Verification

537/537 tests passing, npx tsc --noEmit clean. Both behaviours are covered by new tests written against the failing state first: one asserts a path-bearing baseUrl is rejected while bare origins (with port, with trailing slash) still pass, the other drives a real failing relay through the message:received hook and asserts the reason reaches healthCheck.

A relay that could not reach Chatwoot recorded its reason only in the host log,
which the dashboard does not render. The health check reported bare counts, so
"1 dead-lettered after 5 attempts" was the whole of what an operator could see —
enough to know something was wrong, not enough to act. The health check now
carries the last relay error alongside the counts, and only while something is
failing, so a healthy plugin does not display a stale one.

Also rejects a baseUrl carrying a path at config-read time. The client appends
/api/v1/accounts/<id> to it, so a URL copied from the dashboard's address bar
enabled cleanly and then 404'd every request, with the failure looking identical
to a wrong token or an unreachable host.
@rmyndharis
rmyndharis force-pushed the fix/chatwoot-adapter-relay-diagnostics branch from 79456a8 to 4812af4 Compare August 5, 2026 14:03
@rmyndharis
rmyndharis merged commit fd0e9c6 into main Aug 5, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Messages not being sent to Chatwoot

1 participant