odysseus-dev/odysseus · error · Error

Failed to load research

Error message

Failed to load research

What it means

Inline grid message shown when loading the research library fails. _renderLibResearch fetches /api/research/library with limit/sort/search params and throws new Error(res.status) on any non-OK response; note the error is created but this outer handler renders 'Failed to load research' (the sibling documents grid shows the analogous text).

Solutions

  1. Read the actual status from the network tab (the thrown Error carries it)
  2. Verify the sort/search values are among the API's accepted values
  3. Re-login if 401; restart/health-check the backend if connection refused
  4. Catch and display e.message (the status) in the empty-state instead of a fixed string

Example fix

// before
  } catch (e) {
    grid.innerHTML = '<div class="doclib-empty">Failed to load research</div>';
  }

// after
  } catch (e) {
    console.error('library research error:', e);
    grid.innerHTML = `<div class="doclib-empty">Failed to load research (${e.message})</div>`;
  }
Defensive patterns

Strategy: try-catch

Validate before calling

const VALID_SORTS = ['newest','oldest'];
if (!VALID_SORTS.includes(_lib.sort)) _lib.sort = 'newest';
if (!navigator.onLine) { grid.innerHTML = '<div class="doclib-empty">Offline</div>'; return; }

Try / catch

try { const res = await fetch(url); if (!res.ok) throw new Error(`HTTP ${res.status}`); ... } catch (e) { grid.innerHTML = `<div class="doclib-empty">Failed to load research (${e.message})</div>`; }

Prevention

When it happens

Trigger: GET /api/research/library?limit=50&sort=...&search=... returns non-2xx — malformed sort param, invalid search encoding, 401 — or the fetch itself rejects (server down, offline).

Common situations: Backend not running or restarted; auth expired while library tab open; an unsupported sort value left in module state; very large search strings breaking the query.

Related errors


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

Appendix: source

Thrown at static/js/sessions.js:3458

        e.stopPropagation();
        _showDropdown(e.currentTarget, [
          { label: 'Open', action: () => { if (d.session_id) { closeLibrary(); selectSession(d.session_id); } } },
          { label: 'Delete', action: async () => { if (!await uiModule.styledConfirm('Delete?', { confirmText: 'Delete', danger: true })) return; await fetch(`${API_BASE}/api/document/${d.id}`, { method: 'DELETE' }); _renderLibGrid(); }, danger: true },
        ]);
      });
      grid.appendChild(card);
    }
  } catch (e) { console.error('Library documents error:', e); grid.innerHTML = '<div class="doclib-empty">Failed to load documents</div>'; }
}

async function _renderLibResearch(grid) {
  grid.innerHTML = '';
  grid.appendChild(spinnerModule.createLoadingRow('Loading research…'));
  try {
    const params = new URLSearchParams({ limit: '50', sort: _lib.sort });
    if (_lib.search) params.set('search', _lib.search);
    const res = await fetch(`${API_BASE}/api/research/library?${params}`);
    if (!res.ok) throw new Error(res.status);
    const data = await res.json();
    const items = data.research || [];
    const statsEl = document.getElementById('lib-stats');
    if (statsEl) statsEl.textContent = `${data.total || 0} research`;
    grid.innerHTML = '';
    if (!items.length) {
      grid.innerHTML = '<div class="doclib-empty">No research found</div>';
      return;
    }
    for (const item of items) {
      const meta = [
        item.duration || '',
        item.rounds ? item.rounds + ' rounds' : '',
      ].filter(Boolean).join(' \u00b7 ');
      const card = _buildLibCard(
        item.id, item.query || '(untitled)', item.source_count || 0,
        meta, item.completed_at ? new Date(item.completed_at * 1000).toISOString() : '',
        false, false,

View on GitHub (pinned to f9235ebbf1)