bytedance/deer-flow · error · SkillRequestError

HTTP ${response.status}: ${response.statusText}

Error message

HTTP ${response.status}: ${response.statusText}

What it means

Thrown when GET /api/skills fails. This lists the skills the backend discovered (public skills plus managed integration packs). SkillRequestError carries the status and the backend 'detail' or 'HTTP <status> <statusText>' fallback. Non-2xx typically means the skills directory is unreadable, extensions_config.json is broken, or the Gateway is not fully booted.

Source

Thrown at frontend/src/core/skills/api.ts:30

    this.status = status;
  }

  get isAdminRequired(): boolean {
    return this.status === 403;
  }
}

async function readErrorDetail(response: Response): Promise<string> {
  const data = (await response.json().catch(() => ({}))) as {
    detail?: string;
  };
  return data.detail ?? `HTTP ${response.status}: ${response.statusText}`;
}

export async function loadSkills() {
  const skills = await fetch(`${getBackendBaseURL()}/api/skills`);
  if (!skills.ok) {
    throw new SkillRequestError(skills.status, await readErrorDetail(skills));
  }
  const json = await skills.json();
  return json.skills as Skill[];
}

export async function enableSkill(skillName: string, enabled: boolean) {
  const response = await fetch(
    `${getBackendBaseURL()}/api/skills/${skillName}`,
    {
      method: "PUT",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        enabled,
      }),
    },
  );

View on GitHub (pinned to 1dd6ba1acb)

Solutions

  1. curl /api/skills and read the 'detail' body — scanner errors name the offending skill file
  2. Validate each custom skill's SKILL.md frontmatter (YAML lint) and fix or remove the broken one
  3. Ensure skills/ and .deer-flow/integrations/skills are present and readable by the Gateway process
  4. Retry after Gateway startup completes (skill scan is logged at boot)

Example fix

// before
const skills = await loadSkills();

// after
const skills = await loadSkills().catch((e) => {
  if (e instanceof SkillRequestError && e.status >= 500) {
    return [] as Skill[]; // degrade UI, surface retry
  }
  throw e;
});
Defensive patterns

Strategy: fallback

Type guard

export function isSkillRequestError(e: unknown): e is SkillRequestError {
  return e instanceof SkillRequestError;
}

Try / catch

try {
  return await loadSkills();
} catch (e) {
  if (isSkillRequestError(e) && e.status >= 500) {
    return retryWithBackoff(() => loadSkills(), {tries: 2}); // Gateway booting
  }
  throw e;
}

Prevention

When it happens

Trigger: Loading the skills page before skill scanning finished (503/500); skills/ directory or .deer-flow/integrations/skills missing or with bad permissions; a malformed SKILL.md frontmatter crashing the scanner (500); nginx 502 during Gateway restart.

Common situations: Cloning the repo without submodules/skills; running in Docker where the skills volume isn't mounted; a custom skill with invalid YAML frontmatter committed to skills/custom/.

Related errors


AI-assisted analysis of bytedance/deer-flow@1dd6ba1acb (2026-08-14). Data as JSON: /api/errors/e905ff4a61436c13. Report an issue: GitHub.