different-ai/openwork · error
Failed to update plugin (${response.status}).
Error message
Failed to update plugin (${response.status}). What it means
Thrown by useUpdatePlugin's updatePlugin mutation when the PATCH to the plugin endpoint returns non-ok. The fallback message includes the HTTP status; a server-provided error message in the payload takes precedence. A 403 'reauth' payload is raised as ReauthRequiredError.
Source
Thrown at ee/apps/den-web/app/(den)/dashboard/_components/plugin-data.tsx:764
}
export function useUpdatePlugin() {
const queryClient = useQueryClient();
const { runReauthableAction } = useOrgDashboard();
return useMutation({
mutationFn: async (input: { pluginId: string; name: string; description: string | null }) => {
await runReauthableAction("update-plugin", async () => {
const { response, payload } = await requestJson(
`/v1/plugins/${encodeURIComponent(input.pluginId)}`,
{
method: "PATCH",
body: JSON.stringify({ name: input.name, description: input.description }),
},
15000,
);
if (!response.ok) {
throw getRequestError(payload, response, `Failed to update plugin (${response.status}).`);
}
});
return input.pluginId;
},
onSuccess: (pluginId) => {
queryClient.invalidateQueries({ queryKey: pluginQueryKeys.detail(pluginId) });
queryClient.invalidateQueries({ queryKey: pluginQueryKeys.list() });
},
});
}
export function useArchivePlugin() {
const queryClient = useQueryClient();
const { runReauthableAction } = useOrgDashboard();
return useMutation({
mutationFn: async (pluginId: string) => {
await runReauthableAction("archive-plugin", async () => {View on GitHub (pinned to 2b7df46e8a)
Solutions
- Read the status: 400 → trim/validate name and description against current limits; 404 → invalidate pluginQueryKeys.all, the plugin is gone.
- On 401/403, re-authenticate or verify plugin-edit permissions (branch on isReauthRequiredError).
- Invalidate pluginQueryKeys.detail(pluginId) and refetch before retrying.
- On 5xx, retry after server recovery; the update is idempotent since PATCH sends full name/description.
Example fix
// before
if (!response.ok) {
throw getRequestError(payload, response, `Failed to update plugin (${response.status}).`);
}
// after
const name = input.name.trim();
if (!name) throw new Error("Plugin name cannot be empty.");
const patched = await requestJson(pluginUrl, { method: "PATCH", body: JSON.stringify({ name, description: input.description }) }, 15000);
if (!patched.response.ok) {
throw getRequestError(patched.payload, patched.response, `Failed to update plugin (${patched.response.status}).`);
} Defensive patterns
Strategy: validation
Validate before calling
const name = input.name.trim();
if (!name) throw new Error("Plugin name is required.");
if (name.length > 100) throw new Error("Plugin name must be 100 characters or fewer.");
if (input.description && input.description.length > 500) throw new Error("Description must be 500 characters or fewer."); Type guard
function isPluginUpdateInput(value: unknown): value is { pluginId: string; name: string; description: string | null } {
if (typeof value !== "object" || value === null) return false;
const v = value as { pluginId?: unknown; name?: unknown; description?: unknown };
return typeof v.pluginId === "string" && v.pluginId.length > 0
&& typeof v.name === "string" && v.name.trim().length > 0
&& (v.description === null || typeof v.description === "string");
} Try / catch
try {
await updatePlugin.mutateAsync({ pluginId, name, description });
} catch (error) {
if (isReauthRequiredError(error)) return startReauth();
if (error.message.includes("(404)")) {
await queryClient.invalidateQueries({ queryKey: pluginQueryKeys.all });
return toast.error("This plugin no longer exists.");
}
toast.error(error.message);
} Prevention
- Trim and validate name/description client-side before PATCHing.
- Refetch the plugin before showing an edit form that may be stale.
- Handle 404 as concurrent deletion and invalidate the plugin list.
- Invalidate pluginQueryKeys.detail(pluginId) after every update attempt.
When it happens
Trigger: Patching a plugin with a rejected name/description (empty, too long, invalid characters — 400), plugin deleted or unpublished by another admin (404), missing edit permission (403), expired token (401), or server error during update.
Common situations: Two admins editing the same plugin where one deletes it; name validation rules tightened in a newer API version; session expiry while the edit dialog was open.
Related errors
- Failed to create the dashboard (${response.status}).
- Failed to update the dashboard (${response.status}).
- Failed to grant plugin access (${response.status}).
- Failed to revoke plugin access (${response.status}).
- Failed to load dashboards (${response.status}).
AI-assisted analysis of different-ai/openwork@2b7df46e8a (2026-09-01).
Data as JSON: /api/errors/56306078081e3fda.
Report an issue: GitHub.