Skip to content

Google Chat Workspace Add-on: Dialog Submit fails (Python HTTP Endpoint) #308

@kiarasha-huma

Description

@kiarasha-huma

I am migrating a legacy Google Chat bot to the new Google Workspace Add-ons using a custom Python HTTP endpoint. I am trying to open a dialog when a user triggers a specific slash command (/k8s_access).

My backend receives the POST request and returns a synchronous JSON response containing the actionResponse envelope to open the dialog. However, the dialog completely fails to render in the Google Chat UI. The error in UI is like this:

Could not load dialog. Either the app is not responding or the response is invalid

My Setup & What I’ve Verified:

App Configuration: In the Google Cloud Console (Workspace Marketplace SDK), the /k8s_access slash command is configured and the “Opens a dialog” checkbox is explicitly enabled.

Routing: My Python HTTP endpoint successfully receives the payload from Google’s service agent, so this is not an IAM/firewall blocking issue in the first request. But in the second attempt, when the user clicks on Submit button, it fails without even calling my backend application.

Response Type: I am returning the JSON synchronously back to the incoming HTTP request, not making a separate asynchronous POST request to the Chat API.

The Code: Here is the Python logic handling the command and building the response. I am using the Cards V2 schema:

def handle_k8s_access_command(event) -> JsonResponse:
    """Handle the /k8s_access command to show a dialog for requesting K8s cluster access."""
    logger.info(f"Received /k8s_access command from {event.user.email}")
    
    dialog = Dialog(
        header=Header(title="Request K8s Access"),
        sections=[
            Section(header="Cluster Details").add_widget(
                TextInput(
                    name="cluster_name",
                    label="Cluster Name",
                    hint="Enter the Kubernetes cluster name"
                )
            )
        ],
        fixed_footer=FixedFooter(
            primary_button=PrimaryButton(
                text="Submit",
                function="handle_submit_k8s_access"
            )
        )
    )
    return JsonResponse(dialog.to_dict())

class Dialog:
    def __init__(
        self,
        header: "Header",
        sections: List["Section"],
        fixed_footer: "FixedFooter" = None,
    ):
        self.header = header
        self.sections = sections
        self.fixed_footer = fixed_footer

    def to_dict(self):
        body = {
            "sections": [section.to_dict() for section in self.sections]
        }
        
        if self.header:
            body["header"] = self.header.to_dict()
            
        if self.fixed_footer:
            body["fixedFooter"] = self.fixed_footer.to_dict()
        
        return {
            "action": {
                "navigations": [
                    {
                        "pushCard": body
                    }
                ]
            }
        }

class FixedFooter:
    def __init__(self, primary_button: "PrimaryButton" = None):
        self.primary_button = primary_button

    def to_dict(self):
        return {
            "primaryButton": self.primary_button.to_dict()
            if self.primary_button
            else None
        }

The Output JSON: This is the exact dictionary being passed to JsonResponse and sent back to Google:

{
  "action": {
    "navigations": [
      {
        "pushCard": {
          "header": {
            "title": "Request K8s Access",
            "imageUrl": "https://storage.googleapis.com/platformgpt-backend/icons/form.png",
            "imageType": "CIRCLE"
          },
          "sections": [
            {
              "header": "Cluster Details",
              "collapsible": false,
              "widgets": [
                {
                  "textInput": {
                    "name": "cluster_name",
                    "label": "Cluster Name",
                    "hintText": "Enter the Kubernetes cluster name"
                  }
                }
              ]
            }
          ],
          "fixedFooter": {
            "primaryButton": {
              "text": "Submit",
              "onClick": {
                "action": {
                  "function": "handle_submit_k8s_access"
                }
              }
            }
          }
        }
      }
    ]
  }
}

I would appreciate any ideas for debugging this it has already taken up a lot of my time

the error log in GCP cloud logging:

{
  "insertId": "SECRET",
  "jsonPayload": {
    "deployment": "projects/chat/keys/SECRET",
    "deploymentFunction": "handle_submit_k8s_access",
    "@type": "type.googleapis.com/google.cloud.gsuiteaddons.logging.v1.GSuiteAddOnsLogEntry",
    "error": {
      "message": "Unspecified error invoking the add-on.",
      "code": 13
    }
  },
  "resource": {
    "type": "g_suite_add_ons",
    "labels": {
      "project_id": "SECRET",
      "deployment": "projects/chat/keys/SECRET"
    }
  },
  "timestamp": "2026-02-22T14:16:46.566624Z",
  "severity": "ERROR",
  "logName": "projects/SECRET/logs/gsuiteaddons.googleapis.com%2Ferrors",
  "receiveTimestamp": "2026-02-22T14:16:47.064463042Z"
}

The log in my backend django, on first attempt (inputing the command k8s_access ):

[22/Feb/2026:17:26:06 +0000] “POST /hook/message/ HTTP/1.1” 200 506 “-” “Google-gsuiteaddons”

And no log (even trace) on submit, I’m pretty sure that clicking on submit do not call the backend.

I even tried to implement it via a message card v2 api (not dialog), it goes exactly the same, it renders the form in a card for user in the chat, but when you click on submit you got another error in google chat UI without even calling the backend:

Metadata

Metadata

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions