different-ai/openwork · error
The plugin was created, but no id was returned.
Error message
The plugin was created, but no id was returned.
What it means
createImportedPlugin POSTs the imported plugin and expects the response payload { item: { plugin: { id } } }. After the request succeeds (2xx) it extracts plugin.id; if the id is missing or not a string it throws this message. The plugin was actually created server-side — the failure is purely in reading the returned id, so retrying would duplicate the plugin.
Source
Thrown at ee/apps/den-web/app/(den)/dashboard/_components/plugin-editor-screen.tsx:221
credentialMode: draft.credentialMode,
description: description.trim() || null,
githubUrl: draft.githubUrl,
marketplaceId: marketplaceId || undefined,
name: name.trim(),
selectedSkillKeys: draft.selectedSkillKeys,
selectedServerKeys: draft.selectedServerKeys,
}),
},
30000,
);
if (!result.response.ok) {
throw getRequestError(result.payload, result.response, "Failed to create the imported plugin.");
}
const item = isRecord(result.payload) && isRecord(result.payload.item) ? result.payload.item : null;
const plugin = item && isRecord(item.plugin) ? item.plugin : null;
pluginId = plugin && typeof plugin.id === "string" ? plugin.id : null;
});
if (!pluginId) throw new Error("The plugin was created, but no id was returned.");
clearPluginImportDraft();
await queryClient.invalidateQueries({ queryKey: pluginQueryKeys.all });
router.push(getPluginRoute(orgSlug, pluginId));
router.refresh();
} catch (error) {
setSaveError(error instanceof Error ? error.message : "Failed to create the imported plugin.");
} finally {
setSaving(false);
}
}
async function createPlugin() {
if (!name.trim()) {
setSaveError("Give your plugin a name.");
return;
}
if (importDraft) {View on GitHub (pinned to 2b7df46e8a)
Solutions
- Inspect the create response in devtools/network to see the actual payload shape and fix the extraction path to match.
- Align den-web and Den server versions so the create-plugin response contract matches.
- Check for a proxy/middleware rewriting or truncating response bodies on /v1/plugins creation.
- Before retrying, list plugins and reuse an existing plugin with the same name to avoid duplicates; make the server always return the id on success.
Example fix
// before: single rigid extraction path, duplicate-prone retry
const item = isRecord(result.payload) && isRecord(result.payload.item) ? result.payload.item : null;
const plugin = item && isRecord(item.plugin) ? item.plugin : null;
pluginId = plugin && typeof plugin.id === "string" ? plugin.id : null;
if (!pluginId) throw new Error("The plugin was created, but no id was returned.");
// after: accept alternate response shapes before giving up
function extractCreatedPluginId(payload: unknown): string | null {
if (!isRecord(payload)) return null;
const item = isRecord(payload.item) ? payload.item : payload;
const plugin = isRecord(item.plugin) ? item.plugin : item;
return typeof plugin.id === "string" ? plugin.id : null;
}
pluginId = extractCreatedPluginId(result.payload);
if (!pluginId) throw new Error("The plugin was created, but no id was returned."); Defensive patterns
Strategy: validation
Validate before calling
function validateCreatePluginResponse(payload: unknown): string | null {
if (!isRecord(payload)) return null;
const item = isRecord(payload.item) ? payload.item : payload;
const plugin = isRecord(item.plugin) ? item.plugin : item;
return typeof plugin.id === "string" && plugin.id.length > 0 ? plugin.id : null;
}
const id = validateCreatePluginResponse(result.payload);
if (!id) throw new Error("The plugin was created, but no id was returned."); Type guard
function isCreatePluginResponse(p: unknown): p is { item: { plugin: { id: string } } } {
if (!isRecord(p) || !isRecord(p.item)) return false;
const plugin = p.item.plugin;
return isRecord(plugin) && typeof plugin.id === "string" && plugin.id.length > 0;
} Try / catch
try {
const id = await createImportedPlugin(draft);
router.push(getPluginRoute(orgSlug, id));
} catch (err) {
if (err instanceof MissingCreatedIdError) {
const existing = await findPluginByName(draft.name); // avoid duplicate on retry
if (existing) { router.push(getPluginRoute(orgSlug, existing.id)); return; }
}
showToast("The plugin may have been created but its id was not returned; check the plugin list.");
} Prevention
- Never blind-retry a create call after this error — the plugin likely exists; reconcile by name first.
- Add a contract test asserting the create-plugin response envelope { item: { plugin: { id } } }.
- Pin den-web and Den server versions together; validate the response shape on upgrade.
- Reject empty/204 responses server-side by always returning the created plugin id.
When it happens
Trigger: Server returns 200/201 with an unexpected payload shape: item missing, item.plugin missing, or plugin.id absent/non-string — typically a den-web/Den server API contract mismatch, a proxy stripping the body, or an empty 204 response.
Common situations: Self-hosted Den server version older/newer than den-web returning a different create-response envelope; a corporate proxy or auth wrapper rewriting the JSON; the endpoint changed from {item:{plugin:{id}}} to a flat shape; race where the response body was truncated.
Related errors
- Profile update response did not include a user.
- Automation run history was invalid.
- Connection details were missing from the worker response.
- Library response was incomplete.
- Endpoint test returned an unexpected response.
AI-assisted analysis of different-ai/openwork@2b7df46e8a (2026-09-01).
Data as JSON: /api/errors/22022704a3d34abe.
Report an issue: GitHub.