{"record":{"id":"1bc88b294a4b9a09","repo":"different-ai/openwork","slug":"skill-detail-response-was-incomplete","errorCode":null,"errorMessage":"Skill detail response was incomplete.","messagePattern":"Skill detail response was incomplete\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"ee/apps/den-web/app/(den)/dashboard/_components/skill-data.tsx","lineNumber":107,"sourceCode":"        requestJson(`/v1/config-objects/${encodedSkillId}/plugins`, { method: \"GET\" }, 15000),\n      ]);\n      if (!response.ok) {\n        throw new Error(getErrorMessage(payload, `Failed to load skill (${response.status}).`));\n      }\n      if (!membershipResult.response.ok) {\n        throw new Error(getErrorMessage(membershipResult.payload, `Failed to verify skill plugin (${membershipResult.response.status}).`));\n      }\n      const belongsToPlugin = isRecord(membershipResult.payload)\n        && Array.isArray(membershipResult.payload.items)\n        && membershipResult.payload.items.some((entry) => (\n          isRecord(entry) && entry.pluginId === pluginId && entry.removedAt === null\n        ));\n      if (!belongsToPlugin) {\n        throw new Error(\"That skill is not part of this plugin.\");\n      }\n      const skill = parseSkillResponse(payload);\n      if (!skill) {\n        throw new Error(\"Skill detail response was incomplete.\");\n      }\n      return skill;\n    },\n  });\n}\n\nexport function useCreateSkill(pluginId: string) {\n  const queryClient = useQueryClient();\n\n  return useMutation({\n    mutationFn: async (draft: SkillDraft): Promise<DenSkill> => {\n      const { response, payload } = await requestJson(\n        \"/v1/config-objects\",\n        {\n          method: \"POST\",\n          body: JSON.stringify(createSkillPayload(pluginId, draft)),\n        },\n        15000,","sourceCodeStart":89,"sourceCodeEnd":125,"githubUrl":"https://github.com/different-ai/openwork/blob/2b7df46e8ae1517d64c896c7793d2d52ec845669/ee/apps/den-web/app/(den)/dashboard/_components/skill-data.tsx#L89-L125","documentation":"After the membership check passes, useSkill runs parseSkillResponse(payload) on the skill-detail API response. parseSkillResponse returns null when the payload does not match the expected skill shape, and the hook then throws 'Skill detail response was incomplete.' This guards downstream UI against partially-formed skill objects.","triggerScenarios":"GET skill detail returns 200 but the body is missing required fields (e.g. no id/name/steps) or is not a record, so parseSkillResponse returns null.","commonSituations":"Server deployed with a newer/older skill schema than the web client expects; an intermediary (proxy/gateway) returns an empty or HTML error body with 200; API regression truncates the detail payload.","solutions":["Log the raw payload at the failing call and compare against parseSkillResponse's required fields to identify the missing property.","Update the client schema/parse function to match the current API version (or pin/upgrade the API deployment so schemas align).","Confirm no proxy/auth redirect is replacing the JSON body (check Content-Type and body via network tab).","Retry after the API fix; if transient, invalidate the react-query cache and refetch."],"exampleFix":"// before\nconst skill = parseSkillResponse(payload);\nif (!skill) throw new Error(\"Skill detail response was incomplete.\");\n// after\nconst skill = parseSkillResponse(payload);\nif (!skill) throw new Error(`Skill detail response was incomplete: ${JSON.stringify(payload).slice(0, 200)}`);","handlingStrategy":"validation","validationCode":"function looksLikeSkill(v: unknown): boolean {\n  return isRecord(v) && typeof v.id === \"string\" && typeof v.name === \"string\";\n}","typeGuard":"function isSkill(v: unknown): v is Skill {\n  return isRecord(v) && typeof v.id === \"string\" && typeof v.name === \"string\" && Array.isArray(v.steps);\n}","tryCatchPattern":"try {\n  const skill = await skillQuery.refetch();\n} catch (e) {\n  if (e instanceof Error && e.message.includes(\"Skill detail response was incomplete\")) {\n    console.error(\"unexpected payload shape\", e);\n    // show generic load-error state\n  } else throw e;\n}","preventionTips":["Keep parseSkillResponse in sync with the API schema and add a contract test","Validate Content-Type is application/json before parsing","Log the raw payload when parsing fails to speed diagnosis"],"tags":["api","schema","response-parsing"],"backgroundTag":"schema-validation-failed","analyzedSha":"2b7df46e8ae1517d64c896c7793d2d52ec845669","analyzedAt":"2026-09-01T07:59:23.713Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}