From c0a16fb8d474c670af86ff2cb9f90c2dc97d7d30 Mon Sep 17 00:00:00 2001 From: Oskar Haarklou Veileborg Date: Mon, 19 Oct 2026 11:21:51 +0200 Subject: [PATCH] fix(api): show the Socket API's error message on failed requests sendApiRequest and queryApiSafeTextWithStatus now include the `error.message` the Socket API returns in its error body, next to the generic per-status guidance they already gave. The API uses it to explain failures the status code alone can't, such as a version spec it can't serve. getDefaultApiBaseUrl always falls back to the default API URL, so its return type is now `string` and the unreachable "not configured" branches are gone. --- src/utils/api.mts | 38 +++++++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/src/utils/api.mts b/src/utils/api.mts index 0acb94935..a27c88e61 100644 --- a/src/utils/api.mts +++ b/src/utils/api.mts @@ -215,6 +215,26 @@ function _httpsRequestFetch( }) } +/** + * The cause for a failed request to `path`, with the explanation the Socket + * API gives in its `{ error: { message } }` body when there is one. + */ +async function describeApiFailure( + response: Response, + path: string, +): Promise { + let serverMessage: unknown + try { + serverMessage = (JSON.parse(await response.text()) as any)?.error?.message + } catch { + // A body that isn't JSON carries no message. + } + const reason = await getErrorMessageForHttpStatusCode(response.status) + return typeof serverMessage === 'string' && serverMessage + ? `${response.statusText}: ${serverMessage} (reason: ${reason}) (path: ${path})` + : `${response.statusText} (reason: ${reason}) (path: ${path})` +} + export async function apiFetch( url: string, init: ApiFetchInit = {}, @@ -259,7 +279,7 @@ function logPermissionsFor403(cmdPath?: string | undefined): void { } // The Socket API server that should be used for operations. -export function getDefaultApiBaseUrl(): string | undefined { +export function getDefaultApiBaseUrl(): string { const baseUrl = constants.ENV.SOCKET_CLI_API_BASE_URL || getConfigValueOrUndef(CONFIG_KEY_API_BASE_URL) @@ -501,10 +521,6 @@ export async function handleApiCallNoSpinner( async function queryApi(path: string, apiToken: string) { const baseUrl = getDefaultApiBaseUrl() - if (!baseUrl) { - throw new Error('Socket API base URL is not configured.') - } - const url = `${baseUrl}${baseUrl.endsWith('/') ? '' : '/'}${path}` const result = await apiFetch(url, { method: 'GET', @@ -601,7 +617,7 @@ export async function queryApiSafeTextWithStatus( return { ok: false, message: 'Socket API error', - cause: `${result.statusText} (reason: ${await getErrorMessageForHttpStatusCode(status)}) (path: ${path})`, + cause: await describeApiFailure(result, path), data: { code: status, }, @@ -697,14 +713,6 @@ export async function sendApiRequest( } const baseUrl = getDefaultApiBaseUrl() - if (!baseUrl) { - return { - ok: false, - message: 'Configuration Error', - cause: - 'Socket API endpoint is not configured. Please check your environment configuration.', - } - } const { body, commandPath, description, method } = { __proto__: null, @@ -769,7 +777,7 @@ export async function sendApiRequest( return { ok: false, message: 'Socket API error', - cause: `${result.statusText} (reason: ${await getErrorMessageForHttpStatusCode(status)}) (path: ${path})`, + cause: await describeApiFailure(result, path), data: { code: status, },