odysseus-dev/odysseus · error · HTTPException
Failed to import ICS
Error message
Failed to import ICS
What it means
Generic HTTP 500 from POST /import. After the ICS parses successfully, the handler finds or creates the target calendar and imports events (with skip/repair logic) inside a transaction. Any non-HTTP exception during that phase — DB commit failure, a VEVENT with fields that break the row-building code, timezone resolution failure — is rolled back, logged as 'Failed to import ICS: <e>', and returned as this 500. Parse failures of the file itself are the separate 400 'Invalid ICS file' error.
Source
Thrown at routes/calendar_routes.py:1557
)
db.add(ev)
imported += 1
db.commit()
return {
"ok": True,
"imported": imported,
"skipped": skipped,
"repaired": repaired,
"calendar": cal_display,
"calendar_id": target_cal.id,
}
except HTTPException:
raise
except Exception as e:
db.rollback()
logger.error("Failed to import ICS: %s", e)
raise HTTPException(500, "Failed to import ICS")
finally:
db.close()
@router.get("/export/{cal_id}")
async def export_ics(request: Request, cal_id: str):
"""Export a calendar as .ics file."""
from fastapi.responses import Response
owner = _require_user(request)
db = SessionLocal()
try:
cal = _get_or_404_calendar(db, cal_id, owner)
events = db.query(CalendarEvent).filter(
CalendarEvent.calendar_id == cal_id,
CalendarEvent.status != "cancelled",
).all()
lines = [View on GitHub (pinned to f9235ebbf1)
Solutions
- Read the server log ('Failed to import ICS: <e>') — it identifies the event property or DB operation that failed.
- If the error is timezone-related, install tzdata (pip install tzdata / system zoneinfo) in the runtime.
- Split the ICS file and import halves to isolate the offending VEVENT, then fix or drop it.
- For lock contention, import when the sync worker is idle or enable WAL.
Defensive patterns
Strategy: try-catch
Validate before calling
const text = await file.text();
if (!/BEGIN:VCALENDAR/.test(text)) throw new Error('Invalid ICS'); // avoids the 400 variant
if (file.size > 10 * 1024 * 1024) throw new Error('File exceeds 10 MB limit'); // avoids the size error Try / catch
try { await api.post('/import', formData); }
catch (e) {
if (e.status === 500) {
// parse succeeded; a specific VEVENT broke import — check log, split file to isolate
throw e;
}
if (e.status === 400) showError(e.detail);
else throw e;
} Prevention
- Ensure tzdata is installed in the deployment image before importing timezone-heavy exports.
- On 500, bisect the ICS file (import halves) to find the offending VEVENT.
- Import when background CalDAV sync is idle to avoid commit contention.
When it happens
Trigger: A VEVENT whose DTSTART timezone cannot be resolved (missing tzdata, nonstandard TZID); a property type the import code does not handle (PERIOD duration values, weird escaping); database locked at commit by the CalDAV worker; constraint violation when inserting an imported event.
Common situations: Importing exports from niche calendar clients with nonstandard TZIDs; large imports colliding with background sync on SQLite; tzdata not installed in a slim container so timezone lookups fail per-event.
Related errors
- Failed to export ICS
- Failed to delete calendar
- Failed to list calendars
- Failed to list events
- Failed to create event
AI-assisted analysis of odysseus-dev/odysseus@f9235ebbf1 (2026-08-14).
Data as JSON: /api/errors/e8cdfe9a6aab2157.
Report an issue: GitHub.