You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
When a Rocket.Chat App responds to a viewSubmit UIKit interaction with a bare
success response ({ "success": true }, no type field — this is what
apps-engine's UIKitInteractionResponder.successResponse() produces, and is a
documented valid response shape for "done, nothing more to render"), the modal
fails to close on the mobile app. No error is shown to the user; the submit
spinner simply stops and the form stays open.
This worked correctly before PR #7057 ("feat: Voice message blocks"), and still
works correctly on the desktop/web client.
Steps to reproduce
Build (or use) a Rocket.Chat App that opens a UIKit modal (e.g. via a slash
command) and, on viewSubmit, returns context.getInteractionResponder().successResponse().
Open the modal on the mobile app and tap Submit.
Expected: the modal closes, same as it does on desktop/web. Actual: the modal stays open. Nothing is shown to the user — no error, no
update — the loading indicator just clears.
Root cause
In app/lib/methods/actions.ts, triggerAction() parses the response body and
does:
toServerModalInteractionType (app/containers/UIKit/interactionAdapters.ts) is
a strict allow-list of 'modal.open' | 'modal.update' | 'modal.close' | 'errors'.
A bare { success: true } response has interactionType === undefined, so
toServerModalInteractionType('') returns null, and the code above throws.
That exception propagates out of triggerAction, through triggerSubmitView
(app/lib/methods/triggerActions.ts), which has no try/catch of its own — so
its Navigation.back() call (gated on triggerAction's resolved value) is
never reached. The exception is only caught by ModalBlockView.submit()'s
outer catch (e) { /* do nothing */ }, which swallows it silently.
Before #7057, triggerAction/handlePayloadUserInteraction treated any
unrecognized or missing type as an implicit ModalActions.CLOSE by fallthrough
(no allow-list, no throw), so this exact response used to close the modal.
Cancel is unaffected because ModalBlockView.cancel() calls Navigation.back()
unconditionally before making the network call, so the same exception (thrown
inside triggerCancel → triggerAction) no longer matters by the time it fires.
Web is unaffected because ActionManager.emitInteraction's finally block
closes the view on any response type outside an exclusion list
(errors/modal.update/contextual_bar.update), rather than requiring the
type to be on an allow-list.
Proposed fix
In triggerAction, treat a successful response with no type field as an
explicit modal.close before consulting the allow-list, instead of throwing:
When a Rocket.Chat App responds to a
viewSubmitUIKit interaction with a baresuccess response (
{ "success": true }, notypefield — this is whatapps-engine's
UIKitInteractionResponder.successResponse()produces, and is adocumented valid response shape for "done, nothing more to render"), the modal
fails to close on the mobile app. No error is shown to the user; the submit
spinner simply stops and the form stays open.
This worked correctly before PR #7057 ("feat: Voice message blocks"), and still
works correctly on the desktop/web client.
Steps to reproduce
command) and, on
viewSubmit, returnscontext.getInteractionResponder().successResponse().Expected: the modal closes, same as it does on desktop/web.
Actual: the modal stays open. Nothing is shown to the user — no error, no
update — the loading indicator just clears.
Root cause
In
app/lib/methods/actions.ts,triggerAction()parses the response body anddoes:
const { type: interactionType, ...data } = parsed;
const modalType = toServerModalInteractionType(interactionType ?? '');
if (!modalType) {
throw new Error(
Unknown modal interaction type: ${interactionType ?? 'undefined'});}
toServerModalInteractionType (app/containers/UIKit/interactionAdapters.ts) is
a strict allow-list of 'modal.open' | 'modal.update' | 'modal.close' | 'errors'.
A bare { success: true } response has interactionType === undefined, so
toServerModalInteractionType('') returns null, and the code above throws.
That exception propagates out of triggerAction, through triggerSubmitView
(app/lib/methods/triggerActions.ts), which has no try/catch of its own — so
its Navigation.back() call (gated on triggerAction's resolved value) is
never reached. The exception is only caught by ModalBlockView.submit()'s
outer catch (e) { /* do nothing */ }, which swallows it silently.
Before #7057, triggerAction/handlePayloadUserInteraction treated any
unrecognized or missing type as an implicit ModalActions.CLOSE by fallthrough
(no allow-list, no throw), so this exact response used to close the modal.
Cancel is unaffected because ModalBlockView.cancel() calls Navigation.back()
unconditionally before making the network call, so the same exception (thrown
inside triggerCancel → triggerAction) no longer matters by the time it fires.
Web is unaffected because ActionManager.emitInteraction's finally block
closes the view on any response type outside an exclusion list
(errors/modal.update/contextual_bar.update), rather than requiring the
type to be on an allow-list.
Proposed fix
In triggerAction, treat a successful response with no type field as an
explicit modal.close before consulting the allow-list, instead of throwing:
const { type: interactionType, ...data } = parsed;
if (interactionType === undefined) {
return ModalActions.CLOSE;
}
const modalType = toServerModalInteractionType(interactionType);
if (!modalType) {
throw new Error(
Unknown modal interaction type: ${interactionType});}
This restores the old behavior for the specific case that regressed, without
weakening the throw for a genuinely unrecognized type string.
Environment