Description
sentry-dart issue getsentry/sentry-dart#4016 reports that the Flutter feedback form always treats a returned id as success, even when the feedback was never actually sent, discarding the user's draft in the process. While investigating that report I compared it against FeedbackForm here and against the browser widget in sentry-javascript, and this repo has a related (though mechanically different) problem.
What I found in FeedbackForm.tsx
handleFeedbackSubmit calls captureFeedback (from @sentry/core) without awaiting anything, then immediately calls onSubmitSuccess, shows the success alert, and marks _didSubmitForm = true:
captureFeedback(userFeedback, attachments ? { attachments } : undefined);
onSubmitSuccess({ ... });
feedbackAlertDialog(text.successMessageText, '');
onFormSubmitted();
this._didSubmitForm = true;
captureFeedback in @sentry/core is synchronous and fire-and-forget — it just calls scope.captureEvent(...) and returns the generated event id immediately; it does not wait for the transport to actually deliver the envelope. So a network error, a non-2xx response, or beforeSendFeedback dropping the event happens entirely after this function has already reported success to the caller. The catch block only ever fires for a synchronous throw before the event is queued (e.g. a bad argument), not for delivery failure.
The practical effect: users can get "Thank you for your feedback" even though the feedback never reached Sentry, and because _didSubmitForm is true, componentWillUnmount clears the saved draft instead of preserving it — so there's nothing left to retry or recover.
There's also no in-flight guard on the submit button: if a consumer supplies onFormSubmitted (so the form isn't auto-hidden via isVisible: false at L143), repeated taps just fire captureFeedback again with no loading/disabled state.
Contrast with the browser widget
sentry-javascript's own feedback widget (packages/feedback/src/modal/components/Form.tsx + core/sendFeedback.ts) doesn't have this problem: sendFeedback() returns a promise that only resolves after afterSendEvent reports a 2xx, and explicitly rejects on non-2xx/403/a 30s timeout. handleSubmit awaits that, disables the whole <fieldset> while isSubmitting, and only calls onSubmitSuccess after confirmed delivery — on failure it keeps the form open with the data intact and calls onSubmitError.
Suggested fix
Have FeedbackForm await confirmation of actual delivery (mirroring what sendFeedback() does in the browser widget — hooking client.on('afterSendEvent', ...) for the generated event id, or similar) before calling onSubmitSuccess / clearing the draft, and disable the submit control while that's pending.
I'm not proposing a specific implementation here — flagging this now since it came up directly while investigating the Dart issue, and wanted to get it on your radar before scoping a fix.
Description
sentry-dartissue getsentry/sentry-dart#4016 reports that the Flutter feedback form always treats a returned id as success, even when the feedback was never actually sent, discarding the user's draft in the process. While investigating that report I compared it againstFeedbackFormhere and against the browser widget insentry-javascript, and this repo has a related (though mechanically different) problem.What I found in
FeedbackForm.tsxhandleFeedbackSubmitcallscaptureFeedback(from@sentry/core) without awaiting anything, then immediately callsonSubmitSuccess, shows the success alert, and marks_didSubmitForm = true:captureFeedbackin@sentry/coreis synchronous and fire-and-forget — it just callsscope.captureEvent(...)and returns the generated event id immediately; it does not wait for the transport to actually deliver the envelope. So a network error, a non-2xx response, orbeforeSendFeedbackdropping the event happens entirely after this function has already reported success to the caller. Thecatchblock only ever fires for a synchronous throw before the event is queued (e.g. a bad argument), not for delivery failure.The practical effect: users can get "Thank you for your feedback" even though the feedback never reached Sentry, and because
_didSubmitFormistrue,componentWillUnmountclears the saved draft instead of preserving it — so there's nothing left to retry or recover.There's also no in-flight guard on the submit button: if a consumer supplies
onFormSubmitted(so the form isn't auto-hidden viaisVisible: falseat L143), repeated taps just firecaptureFeedbackagain with no loading/disabled state.Contrast with the browser widget
sentry-javascript's own feedback widget (packages/feedback/src/modal/components/Form.tsx+core/sendFeedback.ts) doesn't have this problem:sendFeedback()returns a promise that only resolves afterafterSendEventreports a 2xx, and explicitly rejects on non-2xx/403/a 30s timeout.handleSubmitawaits that, disables the whole<fieldset>whileisSubmitting, and only callsonSubmitSuccessafter confirmed delivery — on failure it keeps the form open with the data intact and callsonSubmitError.Suggested fix
Have
FeedbackFormawait confirmation of actual delivery (mirroring whatsendFeedback()does in the browser widget — hookingclient.on('afterSendEvent', ...)for the generated event id, or similar) before callingonSubmitSuccess/ clearing the draft, and disable the submit control while that's pending.I'm not proposing a specific implementation here — flagging this now since it came up directly while investigating the Dart issue, and wanted to get it on your radar before scoping a fix.