different-ai/openwork · error

Failed to load skill (${response.status}).

Error message

Failed to load skill (${response.status}).

What it means

Thrown inside useSkill's TanStack Query fetcher when the GET /v1/config-objects/{id} request returns a non-ok status. getErrorMessage(payload, fallback) is used so the server-provided error message surfaces when present, with the status-code string as the fallback. This is the primary skill-detail fetch failing, which leaves the skill page in an error state.

Source

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

function parseSkillResponse(payload: unknown): DenSkill | null {
  return isRecord(payload) ? parseSkillPayload(payload.item) : null;
}

export function useSkill(pluginId: string, skillId: string) {
  const { orgId } = useOrgDashboard();
  const organizationId = orgId ?? "none";

  return useQuery({
    enabled: Boolean(orgId && pluginId && skillId),
    queryKey: skillQueryKeys.detail(organizationId, pluginId, skillId),
    queryFn: async (): Promise<DenSkill> => {
      const encodedSkillId = encodeURIComponent(skillId);
      const [{ response, payload }, membershipResult] = await Promise.all([
        requestJson(`/v1/config-objects/${encodedSkillId}`, { method: "GET" }, 15000),
        requestJson(`/v1/config-objects/${encodedSkillId}/plugins`, { method: "GET" }, 15000),
      ]);
      if (!response.ok) {
        throw new Error(getErrorMessage(payload, `Failed to load skill (${response.status}).`));
      }
      if (!membershipResult.response.ok) {
        throw new Error(getErrorMessage(membershipResult.payload, `Failed to verify skill plugin (${membershipResult.response.status}).`));
      }
      const belongsToPlugin = isRecord(membershipResult.payload)
        && Array.isArray(membershipResult.payload.items)
        && membershipResult.payload.items.some((entry) => (
          isRecord(entry) && entry.pluginId === pluginId && entry.removedAt === null
        ));
      if (!belongsToPlugin) {
        throw new Error("That skill is not part of this plugin.");
      }
      const skill = parseSkillResponse(payload);
      if (!skill) {
        throw new Error("Skill detail response was incomplete.");
      }
      return skill;
    },

View on GitHub (pinned to 2b7df46e8a)

Solutions

  1. Check the response status and payload in the network tab to identify 401 vs 404 vs 5xx
  2. Verify the skillId in the URL exists in the target org (config-objects list)
  3. Re-authenticate if 401; confirm the user's role grants read access to config objects if 403
  4. Retry on 5xx / check den-api server health and logs
Defensive patterns

Strategy: try-catch

Validate before calling

// client-side preconditions before firing the request
if (!skillId || skillId.trim().length === 0) throw new Error("A skill id is required.");
if (!orgSlug) throw new Error("Load the skill within an organization context.");
// auth guard
const session = await getSession();
if (!session) throw new Error("Sign in to view this skill.");

Type guard

function isConfigObjectPayload(payload: unknown): payload is { id: string; name: string; [k: string]: unknown } {
  return typeof payload === "object" && payload !== null && "id" in payload && typeof (payload as { id: unknown }).id === "string";
}

Try / catch

const { data, error, refetch } = useSkill(skillId, pluginId);
if (error) {
  if (error.message.includes("404")) return <NotFound onBack={goBack} />;
  if (error.message.includes("401") || error.message.includes("403")) return <SignInPrompt />;
  return <ErrorState message={error.message} onRetry={refetch} />;
}

Prevention

When it happens

Trigger: GET /v1/config-objects/{encodedSkillId} responds 401/403 (no session or no permission for this org's config object), 404 (skill id wrong, skill deleted, or wrong org), or 5xx from the API.

Common situations: Bookmarked/shared URL pointing to a deleted or renamed skill; cross-org access attempt; expired auth token; URL-encoding mismatch producing a wrong id on the server; API outage returning 502/503.

Understand the failure class

Background: 'Something went wrong' / 'Request failed (500)' / 'HTTP error! status: 404' — what failed HTTP requests actually mean and how to find the real cause — 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/256ba3c3a5c2f289. Report an issue: GitHub.