ruvnet/RuView · error · Error

Request failed

Error message

Request failed

What it means

api.service.js throws on any non-ok response, preferring the JSON body's `message` then `detail` fields. 'Request failed' is the last-resort message, used only when the body parsed as JSON but contains neither a truthy `message` nor `detail` — a non-JSON body instead produces the 'HTTP <status>: <statusText>' fallback from the catch.

Source

Thrown at ui/services/api.service.js:109

      // An earlier revision caught the server's RFC 6750 "reauthentication
      // required" challenge and redirected to /oauth/start. That challenge can
      // never be issued to a browser: browser sign-in requests `sensing:read`
      // only and always will (see BROWSER_SIGNIN_SCOPE), so no browser session
      // holds `sensing:admin`, so the freshness gate the challenge announces is
      // never reached. Admin work goes through the CLI or a pasted bearer.
      //
      // Removed rather than left inert, because it was not merely dead — it
      // ended in a promise that never settles. If any other 401 ever grew that
      // header, every caller awaiting this would hang forever with no error.
      // The server-side guard stays as a fail-closed backstop; the client has
      // nothing to do about a flow that does not exist.

      // Handle errors
      if (!processedResponse.ok) {
        const error = await processedResponse.json().catch(() => ({
          message: `HTTP ${processedResponse.status}: ${processedResponse.statusText}`
        }));
        throw new Error(error.message || error.detail || 'Request failed');
      }

      // Parse JSON response
      const data = await processedResponse.json().catch(() => null);
      return data;

    } catch (error) {
      // Only log if not a connection refusal (expected when DensePose API is down)
      if (error.message && !error.message.includes('Failed to fetch')) {
        console.error('API Request Error:', error);
      }
      throw error;
    }
  }

  // GET request
  async get(endpoint, params = {}, options = {}) {
    const url = buildApiUrl(endpoint, params);

View on GitHub (pinned to 4685618388)

Solutions

  1. Make the server's non-2xx responses include `message` or `detail` (FastAPI's default) so real text surfaces.
  2. Patch the fallback to carry status context: error.message || error.detail || `Request failed (HTTP ${processedResponse.status})`.
  3. When triaging, log processedResponse.status and the raw body to identify which endpoint and envelope shape is involved.

Example fix

// before (ui/services/api.service.js)
throw new Error(error.message || error.detail || 'Request failed');
// after
throw new Error(error.message || error.detail || `Request failed (HTTP ${processedResponse.status} ${processedResponse.statusText})`);
Defensive patterns

Strategy: try-catch

Try / catch

try {
  const data = await apiService.request('/zones', { method: 'GET' });
} catch (error) {
  if (error.message === 'Request failed') {
    // body had no message/detail; surface generic failure with endpoint context
    console.error('Unstructured error response from /zones');
  } else if (error.message?.includes('Failed to fetch')) {
    // DensePose API down / network refused — expected condition
  }
  throw error;
}

Prevention

When it happens

Trigger: Backend or gateway returns non-2xx with a JSON body like {"error":"..."}, {"status":500}, or {"message":""} — envelopes this service does not recognize.

Common situations: Reverse proxies returning JSON errors in their own envelope; a framework version change moving the error text to a different key; production error sanitizers emptying message strings.

Related errors


AI-assisted analysis of ruvnet/RuView@4685618388 (2026-08-16). Data as JSON: /api/errors/a881e4ce94734251. Report an issue: GitHub.