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
- Refresh the client's skill list (GET the skills index) and re-check the exact name before invoking.
- 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.
- 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.
- 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
- Fetch the invocable index immediately before invoking, never from a cached session list.
- Normalize whitespace and casing when matching user-typed slash names.
- On any 404 from invoke, refresh the skill list once instead of retrying.
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
- Skill source unavailable
- Server error ' + res.status
- Integration not found
- Calendar not found
- Event not found
AI-assisted analysis of odysseus-dev/odysseus@f9235ebbf1 (2026-08-14).
Data as JSON: /api/errors/e586a187cbbb0a5c.
Report an issue: GitHub.