Skip to content

fix(backend): skip malformed calendar meetings instead of 500ing the whole list#8865

Merged
kodjima33 merged 2 commits into
BasedHardware:mainfrom
ZachL111:zach/calendar-meetings-poison-guard
Jul 3, 2026
Merged

fix(backend): skip malformed calendar meetings instead of 500ing the whole list#8865
kodjima33 merged 2 commits into
BasedHardware:mainfrom
ZachL111:zach/calendar-meetings-poison-guard

Conversation

@ZachL111

@ZachL111 ZachL111 commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes a poison-record bug: one malformed stored calendar meeting made GET /v1/calendar/meetings return 500, hiding every other meeting the user has.

Root cause

The endpoint built the response with an unguarded list comprehension:

return [CalendarMeetingContext(**m) for m in meetings]

CalendarMeetingContext has required no-default fields (calendar_event_id, title, start_time, duration_minutes). If any stored meeting record is missing one of those or has a bad type, CalendarMeetingContext(**m) raises a ValidationError, which propagates out of the comprehension and 500s the whole request. So a single corrupt record takes down the entire list, not just itself.

Fix

Add a CalendarMeetingContext.from_records factory that parses each record and skips the ones that fail validation, reporting each skipped record through an on_error callback. The list endpoint uses it, logging and skipping a bad record instead of failing the request.

Testing

tests/unit/test_calendar_meeting_from_records.py: valid records are parsed, malformed records are skipped without losing the valid ones (and are reported via on_error), a missing callback is tolerated, and empty input returns an empty list. The factory is pure, so the test needs no I/O.

PR status

Mergeable, no conflicts. Additive resilience: valid records return exactly as before; only malformed records change from 500-the-whole-list to skip-and-log.

Review in cubic

…whole list

GET /v1/calendar/meetings built [CalendarMeetingContext(**m) for m in meetings], and
the model has required no-default fields (calendar_event_id, title, start_time,
duration_minutes). A single malformed stored meeting raised a ValidationError that
propagated out of the comprehension and 500'd the whole request, so one bad record
hid every other meeting the user has.

Add CalendarMeetingContext.from_records, which parses each record and skips the ones
that fail validation (reporting them via on_error), and use it in the list endpoint
so a bad record is logged and skipped instead of failing the request.

Test: tests/unit/test_calendar_meeting_from_records.py (valid parsed, malformed
skipped without losing valid ones, missing-callback tolerated, empty).

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 3 files

Confidence score: 3/5

  • In backend/routers/calendar_meetings.py, the on_error handler logs raw ValidationError text, which can include input_value from malformed meeting payloads; merging as-is risks leaking sensitive user content into application logs. Switch to a sanitized/redacted error log (or structured fields that exclude raw input) before merging to de-risk privacy exposure.

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread backend/routers/calendar_meetings.py Outdated
@Git-on-my-level

Copy link
Copy Markdown
Collaborator

Good fix — thanks @ZachL111. The poison-record problem is real and the from_records approach is clean and well tested (valid parse, malformed skip without losing good records, missing callback tolerated, empty input). All CI green.

One concrete improvement before merge: the on_error handler does logger.warning('...: %s', exc), and the str() of a pydantic ValidationError renders the failing field's input_value. For calendar meetings that value can be sensitive user content (titles, participant emails, meeting links, notes). Consider logging a safe identifier like calendar_event_id plus just the exception class name, so operators can still locate a bad record without writing user content into application logs, e.g.:

on_error=lambda record, exc: logger.warning(
    'Skipping malformed calendar meeting for uid=%s event_id=%s: %s',
    uid, record.get('calendar_event_id'), type(exc).__name__,
),

Flagging for maintainer review on the data-handling point above before merge; otherwise the implementation looks good to me.

@Git-on-my-level Git-on-my-level added the needs-maintainer-review Needs human maintainer review before merge label Jul 2, 2026
…r meeting

A pydantic ValidationError's str() renders the failing field's input_value, which for
a calendar meeting can be sensitive user content (title, participant emails, meeting
link, notes). Log calendar_event_id plus the exception class name instead, so a bad
record is still locatable without writing user content into application logs
(addresses review feedback).
@ZachL111

ZachL111 commented Jul 2, 2026

Copy link
Copy Markdown
Contributor Author

Thanks David, good catch on the logging. Applied your exact suggestion in 0cc1903: the on_error now logs uid, calendar_event_id, and the exception class name (type(exc).name), so a bad record is still locatable without the ValidationError str() writing the meeting title, participant emails, link, or notes into the logs.

@kodjima33 kodjima33 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Backend bug fix w/ tests: skip malformed calendar meeting instead of 500ing the whole list. Established poison-record pattern.

@kodjima33 kodjima33 merged commit a396dcf into BasedHardware:main Jul 3, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs-maintainer-review Needs human maintainer review before merge

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants