#Handle 401 errors in tools calling gracefully

1 messages · Page 1 of 1 (latest)

spiral temple
#

Working with copilotkit v2 on the UI (React), express runtime with mastra on the BE.

Currently the UI passes an "agentic" jwt token to the agent, which is then used in tool calls to a protected API.

I need to handle 401s on tool calls, with a logout event on the UI. That means I need to catch the 401 event on the UI.

My current approach, is throwing a dedicated exception inside of those tools. That causes a "RUN_ERROR" event via ag-ui.

Anyway I could trigger a more detailed "RUN_ERROR" event, perhaps, with a specified code? Looking for any recommendations to handle this nicely.

frail cargoBOT
#

This solution has been AI generated

You shouldn't rely on tool-thrown errors to carry auth intent. In v2 there's no UI-side onError hook yet and RUN_ERROR isn't meant for application-level auth. Instead, return an HTTP 401 from your backend and intercept it on the client transport to trigger logout.

Recommended pattern

  • Use RUN_ERROR only for in-stream agent/tool failures.
  • For auth failures, return HTTP 401 from your Express/Mastra layer.
  • On the UI, intercept the transport error (AG-UI middleware supports RxJS catchError) and dispatch a logout.

Backend (Express/Mastra): propagate a real 401

app.post("/agent/run", async (req, res, next) => {
  try {
    const r = await fetch(PROTECTED_API, { headers: { Authorization: req.headers.authorization! } });
    if (r.status === 401) {
      return res.status(401).json({ code: "AUTH_ERROR_401", message: "Unauthorized" });
    }
    // normal agent/tool flow...
  } catch (e) {
    next(e);
  }
});

Frontend (AG-UI): catch 401 and logout

Use your AG-UI client/middleware observable and intercept HTTP errors with RxJS:

import { catchError, EMPTY } from "rxjs";

agUiStream$ // your AG-UI request/response stream
  .pipe(
    catchError((err) => {
      if (err?.status === 401 || err?.response?.status === 401 || err?.code === "AUTH_ERROR_401") {
        logout();
        return EMPTY; // stop the stream
      }
      throw err;
    })
  )
  .subscribe();

Why not encode a custom RUN_ERROR code?

  • Not supported: the UI currently doesn't expose a dedicated onError callback or structured codes for RUN_ERROR.
  • The provider may swallow raw HTTP responses; surfacing them via middleware catchError is the advised workaround for now.
#

If you must stay in-stream

As a temporary workaround, include "401" (or a recognizable token) in the tool-thrown error message; parse it client-side from the RUN_ERROR and trigger logout. Prefer the HTTP 401 + middleware interception above for a clean separation of concerns.

Was this helpful?

If this solution worked for you, please click on the appropriate option below to help us improve: