Significant-Gravitas/AutoGPT · warning · HTTPException

'{slug}' is a built-in skill and cannot be deleted

Error message

'{slug}' is a built-in skill and cannot be deleted

What it means

Skill-delete endpoint returns 400 when `delete_user_skill` raises BuiltInSkillError — the target slug resolves to a packaged default skill, which is shared across all users and therefore not user-deletable. The exception message is passed through verbatim ('{slug}' is a built-in skill and cannot be deleted). The detail endpoint flags these with is_default=True precisely so UIs can hide destructive affordances.

Source

Thrown at autogpt_platform/backend/backend/api/features/v1.py:2783

    tags=["skills"],
    dependencies=[Security(requires_user)],
)
async def delete_copilot_skill(
    user_id: Annotated[str, Security(get_user_id)],
    name: str = Path(..., description="Slug of the skill to delete"),
) -> dict[str, str]:
    """Delete a user-distilled skill by slug.

    Built-in defaults are not user-deletable — attempting to delete one
    returns 400.  Missing skills return 404 so the UI can reconcile a
    stale list.
    """
    try:
        slug = await delete_user_skill(user_id, name)
    except ValueError as exc:
        raise HTTPException(status_code=400, detail=str(exc))
    except BuiltInSkillError as exc:
        raise HTTPException(status_code=400, detail=str(exc))
    except SkillNotFoundError as exc:
        raise HTTPException(status_code=HTTP_404_NOT_FOUND, detail=str(exc))
    return {"name": slug}


########################################################
#####################  API KEY ##############################
########################################################


@v1_router.post(
    "/api-keys",
    summary="Create new API key",
    tags=["api-keys"],
    dependencies=[Security(requires_user)],
)
async def create_api_key(
    request: CreateAPIKeyRequest,

View on GitHub (pinned to 9c8bb5550f)

Solutions

  1. Filter deletes with the is_default flag from the skill detail/list payload.
  2. If the default is unwanted in the UI, hide it client-side — deletion is intentionally blocked server-side.
  3. Operators wanting default sets changed must change the packaged default skills, not per-user deletes.

Example fix

// before
{skills.map(s => <DeleteButton onClick={() => api.deleteSkill(s.name)} />)}
// after
{skills.map(s => !s.isDefault && <DeleteButton onClick={() => api.deleteSkill(s.name)} />)}
Defensive patterns

Strategy: type-guard

Validate before calling

const detail = await api.getSkillDetail(slug);
if (detail.isDefault) { show('Built-in skills cannot be deleted'); return; }

Type guard

function isDeletableSkill(s: {isDefault?: boolean}): boolean {
  return s.isDefault !== true;
}

Try / catch

try {
  await api.deleteSkill(slug);
} catch (e) {
  if (e.status === 400 && /built-in/i.test(e.detail)) { hideDeleteUiFor(slug); return; }
  throw e;
}

Prevention

When it happens

Trigger: DELETE /skills/{name} where name matches a built-in default skill slug (the same slugs get_default_skill_with_body resolves).

Common situations: UIs rendering a delete button on every list row including defaults; scripts iterating the full skill list and deleting each entry; users attempting to remove unwanted default skills to declutter.

Related errors


AI-assisted analysis of Significant-Gravitas/AutoGPT@9c8bb5550f (2026-08-14). Data as JSON: /api/errors/a58fb3398be99d8e. Report an issue: GitHub.