jdalrymple / gitbeaker

🦊🧪 A comprehensive and typed Gitlab SDK for Node.js, Browsers, Deno and CLI
Other
1.57k stars 299 forks source link

`GitbeakerRequestError.cause.description` has wrong type #3646

Open nhollander-alert opened 1 month ago

nhollander-alert commented 1 month ago

Description

the GitbeakerRequestError object has a property cause.description which is defined in GitbeakerError.ts:4 as a string, however at runtime this value contains an object.

Steps to reproduce

Run the following code snippet (replacing the token and project ID values with any project you have access to).

import { GitbeakerRequestError } from "@gitbeaker/requester-utils";
import { Gitlab } from "@gitbeaker/rest";

const gl = new Gitlab({
    token: "glpat-..."
});

try {
    await gl.Pipelines.create(12345, "invalid_ref");
} catch(e) {
    if(e instanceof GitbeakerRequestError) {
        console.log("Description type:", typeof e.cause?.description);
        console.log("Description value:", e.cause?.description);
    }
}

Expected behaviour

The program should print out

Description type: string
Description value: Reference not found

Actual behaviour

The program will output

Description type: object
Description value: { base: [ 'Reference not found' ] }

Possible fixes

I believe that the issue is caused by the handling of the description field in Requester.ts. The issue could probably be fixed by checking the type of the message field and changing the resolution behavior based on its value.

Gitlab's error message formatting is somewhat inconsistent and not always included in the API documentation so it might be the case that there isn't a guaranteed way to get the core error message, but it might be enough to simply check the type of the message field and if it is not a string, return a JSON stringified version of the response object.

@@ -57,7 +57,9 @@
     if (contentType?.includes('application/json')) {
       const output = JSON.parse(content);

-      description = output.message;
+      description = typeof output.message === "string"
+        ? output.message
+        : JSON.stringify(output);
     } else {
       description = content;
     }

Checklist