odysseus-dev/odysseus · warning · Error
await res.text()
Error message
await res.text()
What it means
HTTP 400 from PUT /{file_id}/vision when the file_id path parameter fails upload_handler.validate_upload_id() — the same ^[0-9a-fA-F]{32}(\.[A-Za-z0-9]+)?$ gate used across the upload routes. On this write route it runs before the index lookup and before any auth check, so a malformed id is always reported as 400 regardless of credentials.
Source
Thrown at static/js/chat.js:241
const pill = document.getElementById('chat-context-pill');
if (!pill) return;
pill.addEventListener('click', (e) => {
e.preventDefault();
e.stopPropagation();
_showContextHeaderPopup();
});
}
export async function compactCurrentChatContext() {
const sm = _liveSessionModule();
const sid = sm && sm.getCurrentSessionId && sm.getCurrentSessionId();
if (!sid) {
uiModule.showToast('Open a chat first');
return false;
}
try {
const res = await fetch(`/api/session/${encodeURIComponent(sid)}/compact`, { method: 'POST' });
if (!res.ok) throw new Error(await res.text());
uiModule.showToast('Context compacted');
_closeContextHeaderPopup();
if (sm && sm.selectSession) await sm.selectSession(sid, { keepSidebar: true, showLoading: false });
refreshChatContextHeader('compact');
return true;
} catch (err) {
uiModule.showError(`Compact failed: ${err.message || err}`);
return false;
}
}
try { window.compactCurrentChatContext = compactCurrentChatContext; } catch (_) {}
export async function refreshChatContextHeader(reason = '') {
_bindContextHeaderPill();
const pill = document.getElementById('chat-context-pill');
if (!pill) return;
const sm = _liveSessionModule();
const sid = sm && sm.getCurrentSessionId && sm.getCurrentSessionId();View on GitHub (pinned to f9235ebbf1)
Solutions
- Send the upload id from the upload response's `id` field verbatim.
- Client-side validate with ^[0-9a-fA-F]{32}(\.[A-Za-z0-9]+)?$ before PUT.
- Log the outbound URL when debugging — most cases are a wrong field or truncation.
- Keep exactly one extension segment if appending one.
Defensive patterns
Strategy: validation
Validate before calling
const UPLOAD_ID_RE = /^[0-9a-fA-F]{32}(\.[A-Za-z0-9]+)?$/;
if (!UPLOAD_ID_RE.test(fileId)) return saveRejected('bad id'); Type guard
const isUploadId = (v) => typeof v === 'string' && /^[0-9a-fA-F]{32}(\.[A-Za-z0-9]+)?$/.test(v); Prevention
- Bind the editor to the attachment's upload id, not its display filename
- Run the id regex before issuing the PUT
When it happens
Trigger: PUT /api/upload/{id}/vision with an id that is not 32-char hex (+optional single extension): truncated id, traversal string, or the client sending a filename.
Common situations: Editing OCR text in the UI where the attachment record was built from the original filename; id mutated by an upstream URL builder; stale client code predating the id format.
Related errors
AI-assisted analysis of odysseus-dev/odysseus@f9235ebbf1 (2026-08-14).
Data as JSON: /api/errors/5c0851367356846f.
Report an issue: GitHub.