rohitg00/agentmemory · warning

[viewer] API error on ${path}:

Error message

[viewer] API error on ${path}:

What it means

The viewer's shared `api(path, ...)` fetch wrapper catches any network/HTTP failure, logs `[viewer] API error on <path>:` with the error, and returns null, which the UI renders as empty data rather than a crash. It fires when fetch itself rejects (daemon down, CORS, aborted request) or when the surrounding code throws before res.json() succeeds (e.g. non-JSON response from a proxy or an auth redirect).

Source

Thrown at src/viewer/index.html:1369

          if (res.status === 401) showViewerAuthPrompt();
          console.warn('[viewer] API ' + (fetchOpts.method || 'GET') + ' ' + path + ' returned ' + res.status);
          // Non-2xx responses resolve to null so callers can keep treating
          // null as "request failed" (e.g. loadGraph's disabled/error state).
          // The health endpoint opts out via readErrorBody: it intentionally
          // responds 503 with a valid JSON body when status is "critical"
          // (see #1019), and the dashboard badge needs that body.
          if (!readErrorBody) return null;
          try {
            return await res.json();
          } catch (parseErr) {
            console.debug('[viewer] API ' + path + ' non-2xx body was not JSON:', parseErr);
            return null;
          }
        }
        hideViewerAuthPrompt();
        return await res.json();
      } catch (err) {
        console.warn('[viewer] API error on ' + path + ':', err);
        return null;
      }
    }
    async function apiGet(path) { return api(path); }
    async function apiPost(path, body) {
      return api(path, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(body || {})
      });
    }
    async function apiDelete(path, body) {
      return api(path, {
        method: 'DELETE',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(body || {})
      });
    }

View on GitHub (pinned to e04ba88819)

Solutions

  1. Confirm the daemon is running and the viewer/REST port matches: curl the same path on the daemon (e.g. curl http://localhost:<port>/agentmemory/health).
  2. Check the browser console/network tab for the actual failing request — status code or CORS/redirect cause.
  3. Re-authenticate with the viewer token if API calls return 401 (the auth prompt appears again).
  4. Fix AGENTMEMORY_URL / proxy configuration so the path returns JSON, not an HTML error page.
  5. Restart the viewer page after restarting the daemon so it picks up the correct base URL.

Example fix

// before
const data = await apiGet('/agentmemory/sessions');
render(data.rows); // crashes on null
// after
const data = await apiGet('/agentmemory/sessions');
if (!data) { showEmptyState('Could not reach agentmemory daemon'); return; }
render(data.rows);
Defensive patterns

Strategy: fallback

Validate before calling

async function daemonReachable(base) {
  try { const r = await fetch(base + '/agentmemory/health', { signal: AbortSignal.timeout(3000) }); return r.ok; }
  catch { return false; }
}

Type guard

function isApiPayload(v) {
  return v !== null && typeof v === 'object' && !Array.isArray(v);
}
const data = await apiGet(path);
if (!isApiPayload(data)) showEmptyState('daemon unreachable or returned non-JSON');

Try / catch

// the viewer api() wrapper already try/catches; consume its null return defensively
const data = await api(path);
if (data === null) {
  renderError('Could not reach the agentmemory daemon — is it running?');
  return;
}

Prevention

When it happens

Trigger: Raised in src/viewer/index.html:1369 whenever apiGet/apiPost is called while the agentmemory daemon is unreachable, the viewer auth prompt was bypassed with an invalid token (401 handling falls through), a proxy intercepts the request and returns non-JSON, or the request is aborted.

Common situations: Daemon not running or restart while the viewer tab stays open; AGENTMEMORY_URL / viewer port misconfigured; a reverse proxy serving an HTML login page instead of JSON; viewer auth token wrong so every call fails; browser blocking mixed content or CORS.

Related errors


AI-assisted analysis of rohitg00/agentmemory@e04ba88819 (2026-08-30). Data as JSON: /api/errors/0dcc223373183bc4. Report an issue: GitHub.