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

  1. 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.
  2. Ensure the frontend and backend are the same version (hard-refresh to bust a stale JS bundle).
  3. Collapse and re-expand the card to retry, since _loaded remains false on failure.
  4. 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

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.