odysseus-dev/odysseus · error · Error
result.detail || 'Failed to rename session'
Error message
result.detail || 'Failed to rename session'
What it means
Raised by _list_github_dir when the GitHub contents API response body is not a JSON array. The endpoint https://api.github.com/repos/<o>/<r>/contents/<path>?ref=<ref> returns a single object (dict) when <path> resolves to a file, and an array when it resolves to a directory — so this error means the traversal reached something that is not a directory listing.
Source
Thrown at static/app.js:1661
body: JSON.stringify({ name: newName })
});
const result = await response.json();
if (response.ok) {
uiModule.showToast(`Session renamed to ${newName}`);
renameSessionModal.classList.add('hidden');
sessionNameInput.value = '';
// Update the current session name in the UI
const meta = sessionModule.getSessions().find(s => s.id === sessionModule.getCurrentSessionId());
if (meta) {
meta.name = newName;
const ver = window._appVersion ? ` v${window._appVersion}` : '';
el('current-meta').textContent = `Session: ${meta.name}${meta.model ? ' ' + meta.model.split('/').pop() : ''}${meta.rag ? ' [RAG]' : ''}${ver}`;
}
// Refresh the sessions list
await sessionModule.loadSessions();
} else {
throw new Error(result.detail || 'Failed to rename session');
}
} catch (e) {
uiModule.showError('Failed to rename session: ' + e.message);
}
});
}
if (closeMemoryBtn) {
closeMemoryBtn.addEventListener('click', () => {
dismissModal(memoryModal);
});
}
// Sidebar Memory button
const toolMemoryBtn = el('tool-memory-btn');
if (toolMemoryBtn && memoryModal) {
toolMemoryBtn.addEventListener('click', () => {
memoryModal.classList.remove('hidden');View on GitHub (pinned to f9235ebbf1)
Solutions
- Link the folder that contains SKILL.md (.../tree/<branch>/my-skill), or SKILL.md itself (.../blob/<branch>/my-skill/SKILL.md)
- Do not link arbitrary files (README.md, config files) — the importer only understands skill folders and SKILL.md files
- If the target is a submodule, link the submodule's real repository URL instead
- Check the URL path for typos — a mistyped folder name can resolve to a file of that name in the repo
Example fix
# before url = "https://github.com/acme/skills/tree/main/skills/debug-helper/README.md" bundle = fetch_skill_bundle(url) # 'expected a directory on GitHub' # after url = "https://github.com/acme/skills/tree/main/skills/debug-helper" bundle = fetch_skill_bundle(url)
Defensive patterns
Strategy: validation
Validate before calling
import httpx
def points_at_directory(owner: str, repo: str, ref: str, path: str, token: str | None = None) -> bool:
"""Ask the contents API whether <path> is a directory before importing."""
url = f"https://api.github.com/repos/{owner}/{repo}/contents/{path}?ref={ref}"
h = {"Accept": "application/vnd.github+json"}
if token:
h["Authorization"] = f"Bearer {token}"
r = httpx.get(url, headers=h, timeout=30.0)
return r.status_code == 200 and isinstance(r.json(), list) Try / catch
try:
files, src = fetch_skill_bundle(url)
except SkillImportError as e:
if "expected a directory" in str(e):
report(f"{url} targets a file — link the folder containing SKILL.md or the SKILL.md blob URL")
return None
raise Prevention
- Only link folders that contain SKILL.md, or SKILL.md blob URLs
- Pre-flight the URL with the contents API to confirm it returns a list
- Reject URLs whose last segment has a file extension other than skill.md in your own UI
When it happens
Trigger: The URL path targets a file whose name does not end in skill.md (fetch_skill_bundle only short-circuits the file case for *.skill.md, everything else goes into _list_github_dir), or the API returns an unexpected object shape (error payload with 200, or a submodule/symlink entry resolved as an object). A repo root that is empty of directories can also return []-vs-object edge shapes on unusual refs.
Common situations: User pastes a /tree/<branch>/path/to/readme.md or /blob/... URL for a non-SKILL.md file; a ref that points at a tag whose tree layout confuses the caller; linking a path that is actually a submodule.
Related errors
- d && d.detail ? d.detail : ('HTTP ' + res.status)
- Invalid backup file: ' + e.message
- Import failed: server returned ${res.status}
- str(e)
- unsafe path: {rel!r}
AI-assisted analysis of odysseus-dev/odysseus@f9235ebbf1 (2026-08-14).
Data as JSON: /api/errors/81d3ea56f40d23db.
Report an issue: GitHub.