Release 2.0.16: backport channelData optional-subfield fix (#564) - #566
Merged
Conversation
Fixes #563 The Teams service can send nested `channelData` objects as empty objects ( `{}` ). Because `AppInfo.id` was required, Pydantic raised a `ValidationError`, and since `ActivityTypeAdapter.validate_python` has no fallback, the entire activity was rejected, so affected bots silently stopped responding. Reported against 2.0.15, the first release containing #504. Confirmed to affect customers, currently in 1:1 chats, with exposure growing as an upstream server-side rollout ramps. ## Root cause `channelData.app` is optional, so an absent `app` was always fine, but a present but empty one wasn't. Auditing the model graph showed this isn't unique to `app`: 98 sites exist where an optional parent points at a child with required fields. Seven were reachable from a plain message activity: ``` channelData.app={} -> channelData.app.id Field required channelData.channel={} -> channelData.channel.id Field required channelData.team={} -> channelData.team.id Field required channelData.tenant={} -> channelData.tenant.id Field required channelData.settings={} -> channelData.settings.selectedChannel attachments=[{}] -> attachments.0.contentType entities=[{}] -> entities.0._unknown.type ``` ## Change Make these inbound-only fields optional: `AppInfo.id`, `ChannelInfo.id`, `TeamInfo.id`, `TenantInfo.id`, `ChannelDataSettings.selected_channel`, `Attachment.content_type`, `EntityBase.type`. `MessageUpdateChannelData` and `MessageDeleteChannelData` both inherit `ChannelData`, so they're covered without separate edits. `EntityBase.type` is relaxed at the base rather than overridden on `UnknownEntity`: every concrete entity already overrides `type` with a `Literal` default, so `UnknownEntity` is the only class inheriting it raw. This also means an entity type introduced after this SDK version no longer drops the message. ## Tests New `test_empty_inbound_objects.py` covering each empty-object case, an unrecognized entity type, that populated values still parse, and that absent `channelData` is unchanged. ## Validation - 852 tests pass across `packages/api` + `packages/apps` - `ruff format` / `ruff check` clean - pyright: zero net new errors ## Notes This is the defensive half. The empty object is a service-side wire-contract issue and has been raised with that team separately; this change means the SDK degrades gracefully regardless. (cherry picked from commit 5fcd8fa)
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Mehak Bindra (MehakBindra)
approved these changes
Aug 19, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Hotfix release 2.0.16 on the pre-2.1 train.
What
Backports #564 ("Make channelData subfields optional to avoid throw", fixes #563) onto
release/v2.0, and sets the stable version to2.0.16.The Teams service can send nested
channelDataobjects as empty ({}). BecauseAppInfo.idwas required, Pydantic raised aValidationErrorandActivityTypeAdapter.validate_pythonhas no fallback, so the whole activity was rejected and affected bots silently stopped responding.This was a regression from #504, which first shipped in 2.0.15 — so
release/v2.0is directly affected.Commits
Make channelData subfields optional to avoid throw (#564)— cleangit cherry-pick -xof5fcd8fafrommain, zero conflictsSet stable version 2.0.16—version.json2.0.15->2.0.16Fix-only: no other
maincommits since 2.0.15 are included.Validation
Run locally on this branch:
pytest packages— 788 passedpyright— 0 errors, 0 warningsruff check— All checks passednbgv get-version— version core 2.0.16 (the-g<sha>suffix is expected off apublicReleaseRefSpecbranch and resolves to plain2.0.16once merged intorelease/v2.0)ruff formatreports drift inexamples/formatted-messaging/src/main.pyandpackages/api/.../quoted_reply_entity.py. Both are pre-existing onrelease/v2.0(they fail at base commitf01b31ftoo) and untouched by this change, so theyre left alone.Merge instructions
Important
Merge with a merge commit, not squash — matching the #546 / #497 precedent on this branch.
After merge: run the ADO publish pipeline on
release/v2.0with Publish Type = Public (ESRP -> PyPI as2.0.16, dist-taglatest), then tag and draft the GitHub release.