mastra-ai/mastra · error · HTTPException

Authentication required

Error message

Authentication required

What it means

The PUT /stored/skills/:storedSkillId/favorite route requires an authenticated caller identity. After the builder 'favorites' feature gate passes, the handler calls getCallerAuthorId(requestContext); if it cannot resolve an author id from the request context it throws HTTPException 401 'Authentication required'. The route is declared requiresAuth: true, so the request also must carry valid auth.

Source

Thrown at packages/server/src/server/handlers/stored-skill-favorites.ts:50

 */
export const FAVORITE_STORED_SKILL_ROUTE = createRoute({
  method: 'PUT',
  path: '/stored/skills/:storedSkillId/favorite',
  responseType: 'json',
  pathParamSchema: storedSkillIdPathParams,
  responseSchema: favoriteToggleResponseSchema,
  summary: 'Favorite a stored skill',
  description: 'Marks the stored skill as favorited by the calling user. Idempotent.',
  tags: ['Stored Skills'],
  requiresAuth: true,
  requiresPermission: 'stored-skills:read',
  handler: async ({ mastra, requestContext, storedSkillId }) => {
    try {
      await requireBuilderFeature(mastra, 'favorites');

      const callerId = getCallerAuthorId(requestContext);
      if (!callerId) {
        throw new HTTPException(401, { message: 'Authentication required' });
      }

      const { skillStore, favoritesStore } = await getFavoritesContext(mastra);

      const skill = await skillStore.getByIdResolved(storedSkillId);
      if (!skill) {
        throw new HTTPException(404, { message: `Stored skill with id ${storedSkillId} not found` });
      }
      assertStoredResourceScope(skill, await getStoredResourceScope(mastra, requestContext));

      // Throws 404 if the caller cannot read the skill (private + not owner/admin).
      assertReadAccess({ requestContext, resource: 'stored-skills', resourceId: storedSkillId, record: skill });

      const result = await favoritesStore.favorite({
        userId: callerId,
        entityType: 'skill',
        entityId: storedSkillId,
      });

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Attach valid credentials to the request (Authorization: Bearer <token> or the server's configured auth mechanism)
  2. Refresh or re-obtain an expired token before retrying
  3. Verify the server's auth configuration resolves the principal to an author id (user/identity mapping)
  4. Ensure the request actually flows through the auth middleware so requestContext contains the caller identity

Example fix

// before
await fetch(`${serverUrl}/stored/skills/${id}/favorite`, { method: 'PUT' });

// after
await fetch(`${serverUrl}/stored/skills/${id}/favorite`, {
  method: 'PUT',
  headers: { Authorization: `Bearer ${token}` },
});
Defensive patterns

Strategy: validation

Validate before calling

if (!token || isTokenExpired(token)) {
  await refreshToken(); // before calling the API
}

Try / catch

try {
  return await api.put(`/stored/skills/${id}/favorite`);
} catch (e) {
  if (isHttpError(e) && e.status === 401) { redirectToLogin(); return null; }
  throw e;
}

Prevention

When it happens

Trigger: Calling PUT /stored/skills/:id/favorite without an Authorization header/session, with an expired or malformed token, or with credentials that don't map to a caller author id in requestContext (getCallerAuthorId returns undefined).

Common situations: Scripts or CI hitting the API with no auth configured; token expired between calls; internal service calls that bypass the auth middleware so requestContext has no user; misconfigured auth provider so the authenticated principal has no authorId.

Understand the failure class

Related errors


AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30). Data as JSON: /api/errors/60f1c992d6db8a21. Report an issue: GitHub.