odysseus-dev/odysseus · warning · Error

HTTP ' + r.status

Error message

HTTP ' + r.status

What it means

HTTP 403 from GET /{file_id}/vision when authentication is configured and effective_user(request) returns no authenticated user. The vision route mirrors the download route's owner-or-admin policy, and anonymous callers are rejected before cache lookup or OCR computation (vision calls can be expensive, so gating early matters).

Source

Thrown at static/js/calendar.js:258

    // Per-event color override (including the bg:<url> sentinel for custom
    // backgrounds) wins over the parent calendar's default hex.
    color: (data.color !== undefined && data.color !== null) ? data.color : (cal?.color || ''),
  };
}

// v2 review error-handling MEDs: every fetch here previously checked
// only `.then(r => r.json())` with no `r.ok` test. A 500/404 still
// resolved the promise and the optimistic state got promoted to truth.
// All three flows now inspect `r.ok` and roll back the optimistic
// state + surface a toast on the failure path.
async function _createEvent(data) {
  const tempUid = 'temp-' + Date.now() + '-' + Math.random().toString(36).slice(2, 8);
  _allEvents[tempUid] = _optimisticEvent(data, tempUid);
  fetch(`${API_BASE}/api/calendar/events`, {
    method: 'POST', credentials: 'same-origin',
    headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data),
  }).then(async r => {
    if (!r.ok) throw new Error('HTTP ' + r.status);
    return r.json();
  }).then(d => {
    if (d.uid) {
      delete _allEvents[tempUid];
      _allEvents[d.uid] = _optimisticEvent(data, d.uid);
      _saveCache && _saveCache();
      if (_open) _render();
    }
  }).catch((e) => {
    delete _allEvents[tempUid];
    if (_open) _render();
    if (window.uiModule) window.uiModule.showError('Failed to create event: ' + (e?.message || 'unknown'));
  });
  return { uid: tempUid };
}

async function _updateEvent(uid, data) {
  const merged = { ...(_allEvents[uid] || {}), ...data };

View on GitHub (pinned to f9235ebbf1)

Solutions

  1. Send valid credentials with the request (session cookie or token) — same auth context as the chat UI uses.
  2. Ensure the client only calls /vision after a successful auth handshake.
  3. Re-login if the session expired; check cookie attributes if the call comes from a different origin.
  4. Confirm whether auth is intentionally configured — an unexpected users/auth config flips this endpoint from open to 403.
Defensive patterns

Strategy: validation

Validate before calling

const session = await getSession();
if (!session?.user) { await login(); } // then call /vision

Try / catch

try {
  const r = await fetch(`/api/upload/${id}/vision`, { credentials: 'include' });
  if (r.status === 403) { promptLogin(); return; }
} catch (e) { /* offline handling */ }

Prevention

When it happens

Trigger: GET /api/upload/{id}/vision without a session/credential while auth is configured; expired session token; server-side fetch to the vision endpoint missing the auth cookie.

Common situations: Frontend prefetching vision text before login completes; incognito access to a shared link; token invalidation after an auth-secret rotation.

Related errors


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