odysseus-dev/odysseus · warning · Error
Failed to load.
Error message
Failed to load.
What it means
Set as the card's <pre> text in _toggleBuiltinExpand (static/js/skills.js:552) when GET /api/skills/builtin/{name} fails; unlike other handlers the HTTP status is swallowed entirely — the user sees only 'Failed to load.'. The flag card._loaded stays false, so collapsing and re-expanding retries the fetch.
Source
Thrown at static/js/skills.js:552
return card;
});
}
async function _expandBuiltinCard(card, name) {
const grid = card.closest('.doclib-grid');
if (card.classList.contains('doclib-card-expanded')) {
card.classList.remove('doclib-card-expanded');
return;
}
if (grid) grid.querySelectorAll('.doclib-card-expanded').forEach(c => c.classList.remove('doclib-card-expanded'));
card.classList.add('doclib-card-expanded');
if (grid) grid.scrollTop = 0;
const pre = card.querySelector('.skill-md-pre');
if (pre && !card._loaded) {
pre.textContent = 'Loading…';
try {
const res = await fetch(`${API}/api/skills/builtin/${encodeURIComponent(name)}`);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const data = await res.json();
pre.textContent = data.text || '(empty)';
card._loaded = true;
card._text = data.text || '';
card._default = data.default || '';
} catch (e) {
pre.textContent = 'Failed to load.';
}
}
}
function _toggleBuiltinEdit(card, name) {
const preview = card.querySelector('.skill-card-preview');
if (!preview) return;
if (preview.querySelector('.skill-md-editor')) { _saveBuiltinEdit(card, name); return; }
const pre = preview.querySelector('.skill-md-pre');
const ta = document.createElement('textarea');
ta.className = 'skill-md-editor';View on GitHub (pinned to f9235ebbf1)
Solutions
- Run the GET by hand (devtools Network tab) to recover the hidden status: 404 → the builtin no longer exists in this server version, reload the page to re-fetch the builtin list; 401 → re-login.
- Ensure the frontend and backend are the same version (hard-refresh to bust a stale JS bundle).
- Collapse and re-expand the card to retry, since _loaded remains false on failure.
- Improve the message to include the status so users can self-diagnose.
Example fix
// before
} catch (e) {
pre.textContent = 'Failed to load.';
}
// after
} catch (e) {
pre.textContent = `Failed to load (HTTP ${e.message.replace('HTTP ', '')}). Click to retry.`;
} Defensive patterns
Strategy: try-catch
Validate before calling
if (!name || card._loaded) return; // skip fetch when already loaded or name unknown
Try / catch
try { const res = await fetch(`${API}/api/skills/builtin/${encodeURIComponent(name)}`); if (!res.ok) throw new Error(`HTTP ${res.status}`); const data = await res.json(); pre.textContent = data.text || '(empty)'; card._loaded = true; } catch (e) { pre.textContent = `Failed to load (${e.message}). Collapse and re-expand to retry.`; } Prevention
- Put the HTTP status into the failure text — 'Failed to load.' hides the cause.
- Leave card._loaded false on failure so re-expand retries.
- Keep frontend bundle and server version in sync to avoid unknown builtin names.
- Hard-refresh after server upgrades.
When it happens
Trigger: Expanding a built-in skill card in the skills browser: GET /api/skills/builtin/{name}. Fails on 404 (built-in renamed/removed in a server version change), 401 (expired session), or 500 (read error on the packaged builtin file).
Common situations: Frontend bundle (static/js) newer or older than the server's built-in skill set after a partial upgrade; auth cookie expired while the page was open; builtin asset missing from a container image.
Related errors
AI-assisted analysis of odysseus-dev/odysseus@f9235ebbf1 (2026-08-14).
Data as JSON: /api/errors/109c8c51b81cd073.
Report an issue: GitHub.