odysseus-dev/odysseus · warning · HTTPException

Skill is not available for slash invocation

Error message

Skill is not available for slash invocation

What it means

404 from the slash-invocation endpoint: the requested skill_id is not a key in the dict built from skills_manager.index_for(owner). Only skills that appear in the owner's invocable index (and have a non-blank name) can be slash-invoked. This is an ownership/scope filter, not just existence — a skill that exists for another owner is indistinguishable from one that never existed.

Source

Thrown at routes/skills_routes.py:1338

        """Build a skill-pinned prompt for slash-command invocation.

        This is intentionally server-side so availability, ownership, and usage
        accounting use the same rules as the SkillsManager.
        """
        user = _owner(request)
        try:
            body = await request.json()
        except Exception:
            body = {}
        request_text = (body.get("request") or "").strip() if isinstance(body, dict) else ""

        invokable = {
            s.get("name"): s for s in skills_manager.index_for(owner=user)
            if (s.get("name") or "").strip()
        }
        match = invokable.get(skill_id)
        if not match:
            raise HTTPException(404, "Skill is not available for slash invocation")

        name = match.get("name")
        md = skills_manager.read_skill_md(name, owner=user)
        if md is None:
            raise HTTPException(404, "Skill source unavailable")

        skills_manager.record_use(name, owner=user)
        message = (
            "Apply the skill below to my request, following its Procedure / Pitfalls / Verification.\n\n"
            f"--- BEGIN SKILL ---\n{md}\n--- END SKILL ---\n\n"
            + (f"Request: {request_text}" if request_text else "Request: (use the skill as appropriate)")
        )
        return {
            "ok": True,
            "type": "skill",
            "name": name,
            "command": f"/{name}",
            "message": message,

View on GitHub (pinned to f9235ebbf1)

Solutions

  1. Refresh the client's skill list (GET the skills index) and re-check the exact name before invoking.
  2. Confirm the skill still exists for the CURRENT owner: GET /{skill_id} — if that also 404s, the skill is gone or owned by someone else.
  3. If the skill exists via GET but invoke 404s, it is excluded from index_for (blank name or index filter) — fix or re-save the skill so it has a valid name.
  4. Guard the slash UX: on 404 show 'skill no longer available' and offer to reload skills instead of retrying blindly.

Example fix

# before
r = requests.post(f"{base}/api/skills/{name}/invoke", json={"request": req})
r.raise_for_status()

# after
r = requests.post(f"{base}/api/skills/{name}/invoke", json={"request": req})
if r.status_code == 404:
    print(f"Skill '{name}' not invokable — refreshing list")
    skills = requests.get(f"{base}/api/skills").json()
    name = next((s["name"] for s in skills if s["name"] == name), None)
    if name is None:
        raise SystemExit(f"Skill '{name}' was deleted or belongs to another owner")
Defensive patterns

Strategy: validation

Validate before calling

names = {s["name"] for s in client.get(f"{base}/api/skills").json()}
if skill_name not in names:
    skill_name = next((n for n in names if n.lower() == skill_name.lower().strip()), None)
    if skill_name is None:
        raise LookupError(f"'{skill_name}' not invokable for this owner")

Type guard

def is_invokable(skill_name: str, skills: list[dict]) -> bool:
    """True when the name appears with a non-blank name in the owner's index."""
    return any((s.get("name") or "").strip() == skill_name.strip() for s in skills)

Prevention

When it happens

Trigger: POST to /{skill_id}/invoke (slash flow) with a name that is misspelled, has different casing/whitespace, was deleted after the UI list was rendered, belongs to a different owner, or exists on disk but is not present in index_for(user) (e.g. filtered out as a non-invokable/published-draft entry).

Common situations: Stale client-side skill list after another tab or the skills audit pipeline deleted or renamed the skill; multi-user deployments where the skill id was copied from someone else's workspace; trailing whitespace or shell-style escaping of the /name typed by the user.

Related errors


AI-assisted analysis of odysseus-dev/odysseus@f9235ebbf1 (2026-08-14). Data as JSON: /api/errors/e586a187cbbb0a5c. Report an issue: GitHub.