odysseus-dev/odysseus · error · Error
d.error || 'Failed to create calendar'
Error message
d.error || 'Failed to create calendar'
What it means
HTTP 500 from GET /{file_id}/vision when analyze_image_with_vl (src/document_processor.py:390) raises while computing OCR/description — e.g. the vision model is unreachable, credentials invalid, or image decoding fails. The underlying exception is interpolated into the message ('Vision analysis failed: {e}') after being logged, so the response carries the root cause. The cache write afterward is best-effort and never 500s.
Source
Thrown at static/js/calendar.js:2625
if (_open) _render();
});
_sunBtn?.addEventListener('click', () => {
_weekStartSun = true;
localStorage.setItem('cal-week-start', 'sun');
_applyWeekStartActive();
if (_open) _render();
});
// Create a new (local) calendar. Defaults the name + next palette color, then
// reopens the panel so the user can rename it inline and pick a color.
overlay.querySelector('#cal-settings-add')?.addEventListener('click', async (e) => {
const btn = e.currentTarget;
btn.disabled = true;
const color = COLORS[_calendars.length % COLORS.length];
try {
const r = await fetch(`${API_BASE}/api/calendar/calendars?name=${encodeURIComponent('New calendar')}&color=${encodeURIComponent(color)}`, { method: 'POST', credentials: 'same-origin' });
const d = await r.json().catch(() => ({}));
if (!r.ok || !d.ok) throw new Error(d.error || 'Failed to create calendar');
_calendars.push({ name: d.name, href: d.id, color: d.color });
_allEvents = {}; _fetchedRanges = []; localStorage.removeItem(LS_KEY);
_render();
cleanup();
_showCalSettings();
// Focus the new row's name field so it's ready to rename.
setTimeout(() => {
const rows = document.querySelectorAll('#cal-settings-list .cal-settings-row');
const last = rows[rows.length - 1];
const nm = last?.querySelector('.cal-s-name');
if (nm) { nm.focus(); nm.select(); }
}, 30);
} catch (err) {
btn.disabled = false;
if (window.showError) window.showError(err.message || 'Failed to create calendar');
else console.error(err);
}
});View on GitHub (pinned to f9235ebbf1)
Solutions
- Read the response body / server log — the embedded {e} names the actual failure (auth error, timeout, decode error).
- Fix the vision model configuration (endpoint URL, API key, quota) in the environment and retry without force=1 so any existing cache is used.
- If the image itself is corrupt, re-upload the source file; a partially-written upload will fail decode every time.
- Avoid force=1 in automated flows — it bypasses the cache and exposes every request to model availability.
- Treat this as transient where applicable: retry with backoff after restoring model access.
Example fix
// before
const r = await fetch(`/api/upload/${id}/vision?force=1`);
// after
const r = await fetch(`/api/upload/${id}/vision`); // prefer cache; force only on user action Defensive patterns
Strategy: retry
Try / catch
async function getVisionText(id, { force = false, retries = 2 } = {}) {
for (let i = 0; i <= retries; i++) {
const r = await fetch(`/api/upload/${id}/vision${force ? '?force=1' : ''}`, { credentials: 'include' });
if (r.ok) return (await r.json()).text;
if (r.status === 500 && i < retries) { await sleep(1000 * 2 ** i); continue; }
throw new Error(`vision failed: ${await r.text()}`); // body carries root cause
}
} Prevention
- Prefer the cache (omit force=1) whenever a cached result is acceptable
- Surface the embedded {e} message to operators — it distinguishes auth vs outage vs corrupt image
- Retry with backoff for transient model outages; fail fast on auth errors
- Validate the model endpoint and key in deployment smoke tests
When it happens
Trigger: First (uncached) GET /vision for an image while the vision model API is down, the API key is invalid/quota-exhausted, the image is corrupt so decode fails, or the model returns a payload the parser rejects. Cached entries (UPLOAD_DIR/.vision/{id}.txt) never hit this path unless force=1.
Common situations: Missing/rotated VL model credentials in deployment env; vision provider rate limit or outage; corrupt upload (partial write) so PIL/opening the file throws; forcing recompute with ?force=1 during an outage when a good cache existed.
Related errors
- HTTP ' + r.status
- await res.text()
- Stream closed before completion
- Server error ' + res.status
- HTTP ${res.status}
AI-assisted analysis of odysseus-dev/odysseus@f9235ebbf1 (2026-08-14).
Data as JSON: /api/errors/e0fbb4dc53f81ce8.
Report an issue: GitHub.