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

  1. Read the server log ('Failed to import ICS: <e>') — it identifies the event property or DB operation that failed.
  2. If the error is timezone-related, install tzdata (pip install tzdata / system zoneinfo) in the runtime.
  3. Split the ICS file and import halves to isolate the offending VEVENT, then fix or drop it.
  4. 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

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


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