different-ai/openwork · error

Skill create response was incomplete.

Error message

Skill create response was incomplete.

What it means

useCreateSkill POSTs a new skill and, after a 2xx response, runs parseSkillResponse on the body. If the created-skill payload can't be parsed into the expected Skill shape, the mutation throws 'Skill create response was incomplete.' The create may actually have succeeded server-side; only the response parsing failed.

Source

Thrown at ee/apps/den-web/app/(den)/dashboard/_components/skill-data.tsx:132

export function useCreateSkill(pluginId: string) {
  const queryClient = useQueryClient();

  return useMutation({
    mutationFn: async (draft: SkillDraft): Promise<DenSkill> => {
      const { response, payload } = await requestJson(
        "/v1/config-objects",
        {
          method: "POST",
          body: JSON.stringify(createSkillPayload(pluginId, draft)),
        },
        15000,
      );
      if (!response.ok) {
        throw getRequestError(payload, response, `Failed to create skill (${response.status}).`);
      }
      const skill = parseSkillResponse(payload);
      if (!skill) {
        throw new Error("Skill create response was incomplete.");
      }
      return skill;
    },
    onSuccess: async () => {
      await Promise.all([
        queryClient.invalidateQueries({ queryKey: skillQueryKeys.all }),
        queryClient.invalidateQueries({ queryKey: pluginQueryKeys.detail(pluginId) }),
      ]);
    },
  });
}

export function useUpdateSkill(pluginId: string) {
  const queryClient = useQueryClient();

  return useMutation({
    mutationFn: async (input: { skillId: string; draft: SkillDraft }): Promise<DenSkill> => {
      const { response, payload } = await requestJson(

View on GitHub (pinned to 2b7df46e8a)

Solutions

  1. Check the network response body of the create call and compare to parseSkillResponse's required fields.
  2. Align client parser with the current server response (update parseSkillResponse or the server to return required fields).
  3. Before retrying, check whether the skill was actually created (duplicate-name risk) to avoid creating it twice.
  4. If a proxy strips bodies, fix gateway config so JSON bodies pass through on 201.

Example fix

// before
const skill = parseSkillResponse(payload);
if (!skill) throw new Error("Skill create response was incomplete.");
// after
const skill = parseSkillResponse(payload);
if (!skill) {
  console.error("create payload:", payload);
  throw new Error("Skill create response was incomplete.");
}
Defensive patterns

Strategy: validation

Validate before calling

function isCreatableSkillInput(v: unknown): boolean {
  return isRecord(v) && typeof v.name === "string" && v.name.trim().length > 0;
}

Type guard

function isSkillResponse(v: unknown): v is Skill {
  return isRecord(v) && typeof v.id === "string";
}

Try / catch

try {
  await createSkill(input);
} catch (e) {
  if (e instanceof Error && e.message === "Skill create response was incomplete.") {
    // verify whether the skill was actually created before retrying (avoid duplicates)
    await queryClient.invalidateQueries({ queryKey: skillQueryKeys.all });
  } else throw e;
}

Prevention

When it happens

Trigger: POST /skills (plugin-scoped) returns ok but the body lacks fields parseSkillResponse requires (e.g. missing id, name, or steps array), or body is not a JSON record.

Common situations: API version drift after a server deploy changes the create-response shape; a gateway returns an empty 201 body; client parse expectations updated without matching server release.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


AI-assisted analysis of different-ai/openwork@2b7df46e8a (2026-09-01). Data as JSON: /api/errors/0d11a3f8f0d4f74e. Report an issue: GitHub.