Mintplex-Labs/anything-llm · warning
Unauthorized
Error message
Unauthorized
What it means
Deliberate 403 from DELETE /browser-extension/api-keys/:id. In multi-user mode, a non-admin (manager) may only delete keys they own: the code looks the key up by { id, user_id } and a null result means 'no such key owned by you', refusing with Unauthorized. Admins skip the ownership check entirely and can delete any key.
Source
Thrown at server/endpoints/browserExtension.js:209
}
}
);
app.delete(
"/browser-extension/api-keys/:id",
[validatedRequest, flexUserRoleValid([ROLES.admin, ROLES.manager])],
async (request, response) => {
try {
const { id } = request.params;
const user = await userFromSession(request, response);
if (multiUserMode(response) && user.role !== ROLES.admin) {
const apiKey = await BrowserExtensionApiKey.get({
id: parseInt(id),
user_id: user?.id,
});
if (!apiKey) {
return response.status(403).json({ error: "Unauthorized" });
}
}
const { success, error } = await BrowserExtensionApiKey.delete(id);
if (!success) throw new Error(error);
response.status(200).json({ success: true });
} catch (error) {
console.error(error);
response.status(500).json({ error: "Failed to revoke API key" });
}
}
);
}
module.exports = { browserExtensionEndpoints };
View on GitHub (pinned to 3aec848f28)
Solutions
- Refresh GET /browser-extension/api-keys and delete using the numeric id of a key your own user created.
- If the key belongs to another user, ask an admin to perform the delete.
- Confirm you are sending the numeric id in the URL, not the API key string.
Defensive patterns
Strategy: validation
Validate before calling
const myRole = await currentUserRole();
const keys = await listExtensionApiKeys();
const owned = keys.find((k) => String(k.id) === String(id));
if (myRole !== 'admin' && !owned) throw new Error('This key is not yours to revoke - ask an admin'); Type guard
const isNumericId = (v: unknown): v is string | number => (typeof v === 'string' && /^\d+$/.test(v)) || typeof v === 'number';
Try / catch
try { await revokeKey(id); }
catch (e) {
if (e.status === 403) { /* refresh the key list; the id is gone or not owned by you */ }
else throw e;
} Prevention
- Refresh the key list immediately before revoking.
- Send the numeric id, never the key string.
- As a manager, only attempt to revoke keys your user created.
When it happens
Trigger: A manager calling DELETE on a key created by another user; the key was already deleted so the scoped lookup misses; passing the wrong id (or the key string instead of the numeric id), which parseInt turns into NaN so no row ever matches.
Common situations: Multi-admin teams with per-user extension keys; stale key list in the admin UI after someone else revoked a key.
Understand the failure class
Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- Workspace not found
- Failed to fetch API keys
- Failed to create API key
- Failed to revoke API key
- Could not find an API Keys.
AI-assisted analysis of Mintplex-Labs/anything-llm@3aec848f28 (2026-08-18).
Data as JSON: /api/errors/480265467db1377f.
Report an issue: GitHub.