Skip to content

feat(realtime): expose granular server error types and payloads#144

Open
VerioN1 wants to merge 6 commits into
mainfrom
feat/realtime-errors-verbosity
Open

feat(realtime): expose granular server error types and payloads#144
VerioN1 wants to merge 6 commits into
mainfrom
feat/realtime-errors-verbosity

Conversation

@VerioN1
Copy link
Copy Markdown
Contributor

@VerioN1 VerioN1 commented May 20, 2026

Previously, real-time server errors were generic. This change introduces
RealtimeWebSocketErrorType and attaches it, along with the full server
payload, to ServerError objects. New REALTIME_ error codes are
added to DecartSDKError to enable more specific error handling and
debugging for different real-time issues like invalid API keys,
insufficient credits, or moderation violations.


Note

High Risk
Touches core realtime signaling/media paths: changes server error classification/codes and alters LiveKit track publish/subscribe behavior (video-only, configurable publish/room options, new stats API), which could affect existing integrations and streaming quality.

Overview
Realtime errors are now typed and surfaced end-to-end. WebSocket error messages include an optional error_type, which is attached (with the full payload) to ServerError, exported via the public SDK, and mapped to new ERROR_CODES.REALTIME_* for more granular client handling.

Realtime streaming is more configurable and observable. realtime.connect now accepts publishOptions, roomOptions, and an optional remoteVideoElement; MediaChannel publishes video tracks only with merged default+override publish settings, and both publisher and subscriber clients expose getVideoStats() returning enriched sender/receiver metrics.

SDK test page gains a benchmarking harness. packages/sdk/index.html adds selectable/chained LiveKit publish profiles, optional looped file-video input, auto-recording, stats polling, and a Chart.js-based benchmark dashboard with JSON/CSV export.

Reviewed by Cursor Bugbot for commit 48a31d1. Bugbot is set up for automated code reviews on this repo. Configure here.

Previously, real-time server errors were generic. This change introduces
`RealtimeWebSocketErrorType` and attaches it, along with the full server
payload, to `ServerError` objects. New `REALTIME_` error codes are
added to `DecartSDKError` to enable more specific error handling and
debugging for different real-time issues like invalid API keys,
insufficient credits, or moderation violations.
@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented May 20, 2026

Open in StackBlitz

npm i https://pkg.pr.new/@decartai/sdk@144

commit: aca3c82

data?: RealtimeServerErrorData,
cause?: Error,
): DecartSDKError {
const code = data?.errorType ? REALTIME_SERVER_ERROR_CODES[data.errorType] : ERROR_CODES.WEBRTC_SERVER_ERROR;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unrecognized error type causes undefined error code

Medium Severity

If the server sends an error_type value not present in REALTIME_SERVER_ERROR_CODES (e.g., a newly added server error type before the SDK is updated), REALTIME_SERVER_ERROR_CODES[data.errorType] evaluates to undefined at runtime. This produces a DecartSDKError with code: undefined, violating the code: string type contract and potentially breaking downstream error-handling logic. The incoming message is only type-asserted (as IncomingRealtimeMessage) without validation, so unrecognized values can flow through unchecked. A fallback to WEBRTC_SERVER_ERROR when the lookup misses would prevent this.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit aca3c82. Configure here.

Comment thread packages/sdk/index.html
for (const [groupKey, fields] of statsAggregates) {
const group = {};
for (const [field, { sum, count }] of fields) {
group[field] = sum;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stats summary outputs sums instead of averages

Medium Severity

buildStatsSummary assigns sum directly to each field but the UI button says "Print Avg Stats" and the console log says "Stats averages." The count is destructured but never used for division. The aggregateStatObject function carefully accumulates both sum and count specifically to enable averaging, but the final computation group[field] = sum omits the / count division, producing misleading raw totals instead of averages.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 1ea50a6. Configure here.

}
}
return { sender: [], receiver };
},
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Receiver stats logic duplicated across two files

Low Severity

The receiver stats gathering logic (iterating remoteParticipants, filtering by identity prefix, checking instanceof RemoteVideoTrack, calling getReceiverStats()) is identically duplicated between MediaChannel.getVideoStats() and the subscribe client's inline getVideoStats. A bug fix in one location would need to be replicated in the other. This could be extracted into a shared utility.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 1ea50a6. Configure here.

@VerioN1 VerioN1 force-pushed the feat/realtime-errors-verbosity branch from 1ea50a6 to 42ea6a2 Compare May 20, 2026 19:45
Comment thread packages/sdk/index.html
Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 2 potential issues.

There are 5 total unresolved issues (including 3 from previous reviews).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 48a31d1. Configure here.

pauseCount: inbound?.pauseCount,
totalPausesDuration: inbound?.totalPausesDuration,
keyFramesDecoded: inbound?.keyFramesDecoded,
codecMimeType: mime ? mime.slice(mime.indexOf("/") + 1).toUpperCase() : undefined,
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Subscriber stats missing framesPerSecond field unlike publisher

Medium Severity

The enrichReceiverFromReport function in subscribe-client.ts omits framesPerSecond from both its RawInbound type and the merged result, while the equivalent mergeReceiver in media-channel.ts correctly includes it. This means getVideoStats() on the subscribe client will never populate framesPerSecond in receiver stats, creating an inconsistency with the publisher client's stats API.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 48a31d1. Configure here.

Comment thread packages/sdk/index.html

const PROFILE_SHORT_LABELS = {
A: 'A — H.264 economy 1.2 Mbps',
B: 'B — H.264 sharper 2 Mbps @ 20 fps',
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Profile short labels show wrong bitrate and fps

Low Severity

PROFILE_SHORT_LABELS for profiles A and B are inconsistent with their actual configurations. Profile A's label says "1.2 Mbps" but maxBitrate is 1_600_000 (1.6 Mbps). Profile B's label says "20 fps" but maxFramerate is 24. These are the labels shown in the benchmark UI dropdown, so users will select profiles based on incorrect information.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 48a31d1. Configure here.

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.

1 participant