pusher
^5.0.0postconditions8functions6last verified2026-06-24coverage score100%Postconditions: what we check
- trigger · api-errorerrorWhenAny API or network failure: authentication error (401/403), rate limit (429), invalid app configuration (400), server error (5xx), network timeout, or connection failureThrows
Pusher.RequestError with properties: status (HTTP code or undefined for network), error (underlying Error for network failures), body (API response text), url (request URL). Common cases: 403 for bad credentials, 429 for rate limit, 5xx for Pusher outage.Required handlingCaller MUST wrap in try-catch. pusher.trigger() rejects on all API and network failures — there is no auto-retry. Unhandled rejections crash Node.js workers or produce silent data loss in async route handlers. Minimum handling: try { await pusher.trigger(channel, event, data); } catch (err) { if (err instanceof Pusher.RequestError) { console.error('Pusher trigger failed:', err.status, err.body); } throw err; } For fire-and-forget patterns (where trigger is a side effect), at minimum log the error rather than silently dropping it: pusher.trigger(channel, event, data).catch(err => console.error('Pusher failed:', err));costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisibleSources[1] - trigger · trigger-encrypted-multi-channel-errorerrorWhenCalling trigger() with an array of channels where any channel name starts with `private-encrypted-` (E2E encrypted channel) and channels.length > 1. Encrypted channels can only be triggered one at a time.Throws
Plain Error: "You cannot trigger to multiple channels when using encrypted channels". Thrown synchronously inside the returned Promise body before any HTTP request — a try-catch around `await pusher.trigger(...)` catches it as a rejected Promise.Required handlingCaller MUST handle this error path when triggering across multiple channels with mixed channel types. Either pre-filter encrypted channels and call trigger() per channel for those, or document the constraint in the calling layer. try { await pusher.trigger(channels, event, data); } catch (err) { if (err.message.includes('encrypted channels')) { for (const ch of channels) { await pusher.trigger(ch, event, data); } } throw err; } Bypassing this error means the original event silently never sends — a real-time data loss bug masked as a rejected Promise.costmediumin prodimmediate exceptionusers seelost datavisibilitysilent - trigger · trigger-encryption-master-key-missingerrorWhenCalling trigger() on a `private-encrypted-` channel when the Pusher client was constructed without `encryptionMasterKeyBase64`. The library has no fallback — E2E encryption requires a master key, and absence is treated as a programming error.Throws
Plain Error: "Set encryptionMasterKey before triggering events on encrypted channels". Thrown synchronously from encrypt() during the trigger() call body. Rejects the returned Promise — visible to a try-catch around `await pusher.trigger(...)`.Required handlingCaller MUST configure encryptionMasterKeyBase64 in the Pusher() constructor when any encrypted channel will be triggered. This is a setup-time invariant — failing here means the entire encrypted-channel feature is broken at runtime. const pusher = new Pusher({ appId, key, secret, cluster, encryptionMasterKeyBase64: process.env.PUSHER_ENCRYPTION_KEY, }); try { await pusher.trigger('private-encrypted-room', 'msg', payload); } catch (err) { if (err.message.includes('encryptionMasterKey')) { console.error('Pusher encrypted-channel setup error — check config'); } throw err; } Without handling, encrypted-channel events silently fail every time, leaving end-to-end encrypted UX broken across the entire deployment.costhighin prodimmediate exceptionusers seelost datavisibilitysilent - triggerBatch · api-errorerrorWhenAny API or network failure, including rate limit (429), auth error, server error, or network failure. Batch max of 10 events — exceeding it throws before any network call.Throws
Pusher.RequestError (same as trigger) for API/network failures. Plain Error for exceeding batch size limit or invalid event structure.Required handlingCaller MUST wrap in try-catch. A batch failure means ALL events in the batch were not delivered — callers should handle this atomically. try { await pusher.triggerBatch(events); } catch (err) { if (err instanceof Pusher.RequestError) { // All events failed — consider retry or fallback } throw err; }costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisibleSources[6] - sendToUser · api-errorerrorWhenAny API or network failure: invalid user ID format, auth error, server error, network failure, or rate limitThrows
Pusher.RequestError for API/network failures. Plain Error for invalid userId (empty string, non-string value).Required handlingCaller MUST wrap in try-catch. User notification failures should be handled gracefully — the primary action (DB write) may have succeeded even if the real-time notification fails. try { await pusher.sendToUser(userId, event, data); } catch (err) { if (err instanceof Pusher.RequestError) { console.error('Failed to notify user:', userId, err.status); } // Don't re-throw if notification failure should not abort the operation }costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisibleSources[7] - terminateUserConnections · terminate-api-errorerrorWhenAny API or network failure: authentication error (401/403 from Pusher API), server error (5xx), network timeout, or connection failure. Also throws synchronous plain Error for empty or non-string userId before any HTTP call.Throws
Pusher.RequestError for API/network failures (status, body, url properties). Plain Error ("Invalid user id: '...'") for empty string or non-string userId — thrown synchronously before any network request.Required handlingCaller MUST wrap in try-catch. terminateUserConnections() is typically called in ban/kick flows after a DB write. If the call fails (Pusher outage, auth error), the user may remain connected despite being banned in the database — a security gap. try { await pusher.terminateUserConnections(userId); } catch (err) { if (err instanceof Pusher.RequestError) { // Log and consider queuing for retry — user may still be connected console.error('Failed to terminate connections for', userId, err.status); await scheduleRetry(userId); } throw err; } Important: terminateUserConnections() disconnects existing sessions but does NOT prevent reconnection. Pair with blocking the user's authentication endpoint.costhighin prodimmediate exceptionusers seesecurity breachvisibilitysilent - get · get-api-errorerrorWhenAny API or network failure: authentication error (401), forbidden (403, app disabled or quota exceeded), bad request (400, e.g. requesting user_count on non-presence channel), server error (5xx), or network failure/timeout.Throws
Pusher.RequestError with status (HTTP code), body (API error text), url (request URL). Plain Error thrown synchronously if reserved param keys (auth_key, auth_timestamp, auth_version, auth_signature, body_md5) are included in params — throws before any network request.Required handlingCaller MUST wrap in try-catch. get() is typically used to fetch presence channel data or verify channel state before performing an action. A 400 from requesting user_count on a non-presence channel is a programming error; 401/403 indicate misconfigured credentials. try { const response = await pusher.get({ path: '/channels', params: { filter_by_prefix: 'presence-', info: 'user_count' } }); if (response.status === 200) { const body = await response.json(); // use body.channels } } catch (err) { if (err instanceof Pusher.RequestError) { if (err.status === 400) { // Invalid params — likely programming error (e.g., user_count on public channel) } console.error('Pusher GET failed:', err.status, err.body); } throw err; }costlowin prodimmediate exceptionusers seeservice unavailablevisibilityvisible - post · post-api-errorwarningWhenAny API or network failure: authentication error (401/403), bad request (400), payload too large (413, event data exceeds 10KB limit), server error (5xx), or network failure. Throws synchronously for reserved param keys in params.Throws
Pusher.RequestError for all HTTP 4xx/5xx responses and network errors. Plain Error synchronously for reserved params (auth_key, auth_timestamp, etc.) before any network call. Note: HTTP 413 is returned when event data exceeds the 10KB Pusher payload limit — relevant when posting custom event payloads.Required handlingCaller MUST wrap in try-catch. post() is most commonly called indirectly via terminateUserConnections() — see that function's error handling guidance. If calling post() directly, validate path correctness and payload size first. try { const response = await pusher.post({ path: '/events', body: JSON.stringify(payload) }); } catch (err) { if (err instanceof Pusher.RequestError) { if (err.status === 413) { // Payload too large — split or compress } console.error('Pusher POST failed:', err.status, err.body); } throw err; }costlowin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
Sources
Every postcondition cites at least one of these. Grouped by source type; numbered to match the footnotes above.
- [3]pusher.com/docs/channels/using_channelsEncrypted Channels
- [5]pusher.com/docs/channels/using_channelsEncrypted Channels
- [8]pusher.com/docs/channels/server_apiTerminating User Connections
- [10]pusher.com/docs/channels/library_auth_referenceRest Api
- [12]pusher.com/docs/channels/library_auth_referenceRest Api
- [1]github.com/pusher/pusher-http-nodepusher/pusher-http-node
- [2]github.com/pusher/pusher-http-node/blobpusher/pusher-http-node · events.js
- [4]github.com/pusher/pusher-http-node/blobpusher/pusher-http-node · events.js
- [6]github.com/pusher/pusher-http-nodepusher/pusher-http-node
- [7]github.com/pusher/pusher-http-nodepusher/pusher-http-node
- [9]github.com/pusher/pusher-http-node/blobpusher/pusher-http-node · pusher.js
- [11]github.com/pusher/pusher-http-node/blobpusher/pusher-http-node · requests.js
Research notes
Curator notes from SOURCES.md captured when the profile was written so you can verify the reasoning, not just the rules.
Sources: pusher
Behavioral claims in contract.yaml are derived from the following sources.
Primary Sources
Official Library (GitHub README)
URL: https://github.com/pusher/pusher-http-node
The primary API reference for the pusher Node.js server library. Documents:
- All method signatures (
trigger,triggerBatch,sendToUser,get,authorizeChannel,authenticateUser) - Error handling section:
RequestErrorclass withstatus,error,body,urlproperties - Promise-based API (v4+ — callbacks removed)
- Event format and channel constraints (max 100 channels per trigger, max 10 events per triggerBatch)
Postconditions supported:
trigger: api-error— "pusher.trigger will reject the promise with a Pusher.RequestError if the API returns an error status code or there is a network failure"triggerBatch: api-error— same error surface as triggersendToUser: api-error— same error surface
Pusher REST API Reference
URL: https://pusher.com/docs/channels/library_auth_reference/rest-api/
Documents the underlying HTTP API that the Node.js library wraps. Defines error status codes:
- 400 Bad Request — validation errors (invalid channel name, event name too long)
- 401 Unauthorized — invalid app credentials
- 403 Forbidden — not authorized for this operation
- 413 Entity Too Large — message body too large
- 429 Too Many Requests — rate limit exceeded
Pusher Channel Types
URL: https://pusher.com/docs/channels/using_channels/channel-types/
Documents public, private (private-), presence (presence-), and encrypted (private-encrypted-) channels.
Relevant for understanding what trigger and sendToUser operate on.
Pusher User Authentication
URL: https://pusher.com/docs/channels/server_api/authenticating-users/
Documents the authenticateUser and sendToUser API for user-bound messaging.
sendToUser requires clients to have authenticated via pusher-js signin().
Version History Sources
v5.3.2 Release Notes
URL: https://github.com/pusher/pusher-http-node/releases/tag/v5.3.2
"Fixed missing error types for TypeScript support" — RequestError and WebHookError
now properly exported as named TypeScript types (previously not exported).
v5.0.0 Migration
URL: https://github.com/pusher/pusher-http-node/blob/master/CHANGELOG.md
Breaking change: trigger(channel, event, data, socketId) → trigger(channel, event, data, { socket_id }).
The 4th positional string argument for socket_id exclusion became a params object.
v4.0.0 Migration (Callbacks → Promises)
Documented in CHANGELOG: removed callback-based API, all async methods now return Promises.
RequestError.statusCode renamed to RequestError.status.
Real-World Evidence
No real-world TPs confirmed yet — evidence_quality: stub.
TODO: Search GitHub for TypeScript SaaS repos using pusher and confirm TRUE_POSITIVE violations
in production code to upgrade evidence_quality to partial or confirmed.