Skip to content

Guard against nil reply in createReplyForComment callback - #25964

Open
jkmassel wants to merge 2 commits into
trunkfrom
jkmassel/nullable-reply-callback
Open

Guard against nil reply in createReplyForComment callback#25964
jkmassel wants to merge 2 commits into
trunkfrom
jkmassel/nullable-reply-callback

Conversation

@jkmassel

@jkmassel jkmassel commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Summary

  • createReplyForComment's completion is annotated non-null, but its implementation can hand back nil — the reply is re-resolved on the main context via existingObjectWithID:error:, which returns nil when the just-created optimistic comment can't be resolved again (a rare Core Data race).
  • Annotate the callback _Nullable so the type tells the truth, and guard the single caller so a nil reply reports a clean failure instead of silently forwarding nil to uploadComment.

Root Cause

The completion resolves its value via [self.coreDataStack.mainContext existingObjectWithID:replyID error:nil] (CommentService.m:119), which returns nil if the optimistic comment can't be re-resolved. The header (CommentService.h) sits inside NS_ASSUME_NONNULL_BEGIN, so the unannotated Comment * has imported into Swift as a non-optional Comment since the method was refactored into this shape in 2023 (63aa79dedd). Callers therefore couldn't guard the value even though the implementation can produce nil.

It's been harmless so far because the one caller only forwards reply to uploadComment(reply, …) — Objective-C, where messaging nil is a safe no-op. It has never been dereferenced in Swift.

Fix

  • _Nullable on the callback in CommentService.h / .m, so it imports into Swift as Comment?.
  • guard let reply in CommentDetailViewController.createReply — a nil reply resumes the continuation with URLError(.unknown) (the error the method's existing failure branch already uses) rather than messaging nil. uploadComment takes a non-null Comment, so the guard is required once reply is optional.
  • Adding the guard turns that completion into a multi-statement closure, which stops withUnsafeThrowingContinuation's T from being inferred from the nested continuation.resume(). Pin it explicitly with UnsafeContinuation<Void, Error> — the same idiom already used in BlogService+Settings and JetpackConnectionViewModel.

Test plan

  • Builds — the guard let only compiles once _Nullable makes reply import as Comment?, so a green build confirms the annotation and the guard are consistent.
  • Replying to a comment from the comment detail / a notification still posts normally.

There are no existing tests over createReplyForComment (a thin Core Data + callback wrapper), so this contract fix doesn't add one.

Related

Splits a correctness fix out of #25945, which adds the first Swift dereference of this callback (TaggedManagedObjectID(reply)) and would otherwise turn this latent inaccuracy into a reachable crash. Landing this first lets #25945 rebase onto an already-_Nullable callback with the guard in place.

createReplyForComment's completion is annotated non-null but its
implementation can pass nil: it re-resolves the reply on the main context
via existingObjectWithID:error:, which returns nil when the optimistic
comment can't be resolved again (a rare Core Data race).

Annotate the callback _Nullable so it imports into Swift as Comment?, and
guard the single caller so a nil reply resumes with a failure instead of
forwarding nil into uploadComment. Adding the guard makes that completion
a multi-statement closure, so pin the continuation type explicitly
(UnsafeContinuation<Void, Error>) to keep T inferable.
@jkmassel jkmassel added this to the 27.3 milestone Sep 1, 2026
@jkmassel jkmassel self-assigned this Sep 1, 2026
@wpmobilebot

wpmobilebot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor
App Icon📲 You can test the changes from this Pull Request in WordPress by scanning the QR code below to install the corresponding build.
App NameWordPress
ConfigurationRelease-Alpha
Build Number34118
VersionPR #25964
Bundle IDorg.wordpress.alpha
Commite5d2397
Installation URL1figo67q5i8og
Automatticians: You can use our internal self-serve MC tool to give yourself access to those builds if needed.

@wpmobilebot

wpmobilebot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor
App Icon📲 You can test the changes from this Pull Request in Jetpack by scanning the QR code below to install the corresponding build.
App NameJetpack
ConfigurationRelease-Alpha
Build Number34118
VersionPR #25964
Bundle IDcom.jetpack.alpha
Commite5d2397
Installation URL03tqabs0sua30
Automatticians: You can use our internal self-serve MC tool to give yourself access to those builds if needed.

The two sibling failure paths in createReply already DDLogError; the nil-reply guard was the one silent path, leaving a rare local Core Data materialization failure indistinguishable from a network error in the logs.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants