Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion src/core/components/request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,11 @@ export abstract class AbstractRequest<ResponseType, ServiceResponse extends obje
if (headers) request.headers = headers;

// Attach body (if required).
if (request.method === TransportMethod.POST || request.method === TransportMethod.PATCH) {
if (
request.method === TransportMethod.POST ||
request.method === TransportMethod.PATCH ||
request.method === TransportMethod.PUT
) {
const [body, formData] = [this.body, this.formData];
if (formData) request.formData = formData;
if (body) request.body = body;
Expand Down
4 changes: 4 additions & 0 deletions src/core/types/transport-request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ export enum TransportMethod {
* Request will be sent using `PATCH` method.
*/
PATCH = 'PATCH',
/**
* Request will be sent using `PUT` method.
*/
PUT = 'PUT',
Comment on lines +22 to +25
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Check signature method in middleware.ts for PUT handling

rg -n -A 15 'signature\(req: TransportRequest\)' src/transport/

Repository: pubnub/javascript

Length of output: 1212


Signature calculation must include PUT request bodies to prevent authentication failures.

The PUT enum addition is correct, but the signature middleware at src/transport/middleware.ts:69-80 only includes request body in signature calculation for POST and PATCH methods. PUT requests with bodies will have their body excluded from the signature input, causing authentication validation failures.

Update the condition to include TransportMethod.PUT:

if (method === TransportMethod.POST || method === TransportMethod.PATCH || method === TransportMethod.PUT) {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/core/types/transport-request.ts` around lines 22 - 25, The signature
middleware currently only includes request bodies for POST and PATCH when
computing the signature, causing PUT bodies to be omitted; update the
method-check in the signature calculation (the conditional that compares method
to TransportMethod.POST / TransportMethod.PATCH) to also include
TransportMethod.PUT so that requests where method === TransportMethod.PUT
include the body in the signature input.

/**
* Request will be sent using `DELETE` method.
*/
Expand Down
52 changes: 52 additions & 0 deletions src/errors/pubnub-api-error.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,29 @@ export class PubNubAPIError extends Error {
) {
errorData = errorResponse;
status = errorResponse.status;
} else if (
'errors' in errorResponse &&
Array.isArray(errorResponse.errors) &&
errorResponse.errors.length > 0
) {
// Handle DataSync-style structured error responses:
// { errors: [{ errorCode: "SYN-0008", message: "...", path: "/id" }] }
errorData = errorResponse;

const errors = errorResponse.errors as Array<{
errorCode?: string;
message?: string;
path?: string;
}>;

message = errors
.map((e) => {
const parts: string[] = [];
if (e.errorCode) parts.push(e.errorCode);
if (e.message) parts.push(e.message);
return parts.join(': ');
})
.join('; ');
} else errorData = errorResponse;
Comment on lines +146 to 169
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Handle edge case where error items lack both errorCode and message.

If all items in the errors array have only path (no errorCode or message), the resulting message would be empty strings joined by '; ', producing unhelpful output like "; " or an empty string.

Consider filtering out empty results:

Proposed fix
                 message = errors
                   .map((e) => {
                     const parts: string[] = [];
                     if (e.errorCode) parts.push(e.errorCode);
                     if (e.message) parts.push(e.message);
                     return parts.join(': ');
                   })
+                  .filter((s) => s.length > 0)
                   .join('; ');
+
+                if (!message) message = 'Unknown error';
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/errors/pubnub-api-error.ts` around lines 146 - 169, When building the
message from the DataSync-style errors (the block that reads
errorResponse.errors and sets message), skip error items that have neither
errorCode nor message so you don't produce empty tokens; specifically, update
the logic around errors (the const errors = ... and the subsequent message =
errors.map(...).join(...)) to either filter out mapped parts that are empty or
filter out error objects where both e.errorCode and e.message are falsy before
mapping, and then join the remaining non-empty strings into message while
leaving errorData assignment unchanged.


if ('error' in errorResponse && errorResponse.error instanceof Error) errorData = errorResponse.error;
Expand Down Expand Up @@ -229,6 +252,35 @@ export class PubNubAPIError extends Error {
};
}

/**
* Format a user-facing error message for this API error.
*
* When the error contains structured details extracted from the service response
* (e.g., DataSync `errors` array), those details are included in the message.
* Otherwise, falls back to a generic description.
*
* @param operation - Request operation during which error happened.
*
* @returns Formatted error message string.
*/
public toFormattedMessage(operation: RequestOperation): string {
const fallback = 'REST API request processing error, check status for details';

// When errorData contains a structured `errors` array, `this.message` was already
// constructed from it in `createFromServiceResponse` — prefer it over the generic fallback.
if (
this.errorData &&
typeof this.errorData === 'object' &&
!('name' in this.errorData && 'message' in this.errorData && 'stack' in this.errorData) &&
'errors' in this.errorData &&
Array.isArray((this.errorData as Record<string, unknown>).errors)
) {
return `${operation}: ${this.message}`;
}

return fallback;
}

/**
* Convert API error object to PubNub client error object.
*
Expand Down
Loading