GoneException when calling post_to_connection on AWS lambda and API gateway

Question:

I want to send a message to a websocket client when it connects to the server on AWS lambda and API gateway. Currently, I use wscat as a client. Since the response ‘connected’ is not shown on the wscat console when I connect to the server, I added post_to_connection to send a message ‘hello world’ to the client. However, it raises GoneException.

An error occurred (GoneException) when calling the PostToConnection
operation

How can I solve this problem and send some message to wscat when connecting to the server?

My python code is below. I use Python 3.8.5.

import os
import boto3
import botocore

dynamodb = boto3.resource('dynamodb')
connections = dynamodb.Table(os.environ['TABLE_NAME'])

def lambda_handler(event, context):
    domain_name = event.get('requestContext',{}).get('domainName')
    stage       = event.get('requestContext',{}).get('stage')
    connection_id = event.get('requestContext',{}).get('connectionId')
    result = connections.put_item(Item={ 'id': connection_id })

    apigw_management = boto3.client('apigatewaymanagementapi',
                            endpoint_url=F"https://{domain_name}/{stage}")
    ret = "hello world";

    try:
      _ = apigw_management.post_to_connection(ConnectionId=connection_id,
                                             Data=ret)
    except botocore.exceptions.ClientError as e:
      print(e);
      return { 'statusCode': 500,
                    'body': 'something went wrong' }

    return { 'statusCode': 200,
             "body": 'connected'};
Asked By: nemy

||

Answers:

I have found that the GoneException can occur when the client that initiated the websocket has disconnected from the socket and its connectionId can no longer be found. Is there something causing the originating client to disconnect from the socket before it can receive your return message?

My use case is different but I am basically using a DB to check the state of a connection before replying to it, and not using the request context to do that. This error’s appearance was reduced by writing connectionIds to DynamoDB on connect, and deleting them from the table upon disconnect events. Messaging now writes to connectionIds in the table instead of the id in the request context. Most messages go through but some errors are still emitted when the client leaves the socket but does not emit a proper disconnect event which leaves orphans in the table. The next step is to enforce item deletes when irregular disconnections occur. Involving a DB may be overkill for your situation, just sharing what helped me make progress on the GoneException error.

Answered By: massif

Self-answer: you cannot post_to_connection to the connection itself in onconnect.

Answered By: nemy

We need to post to connection after connecting (i.e. when the routeKey is not $connect)

routeKey = event.get('requestContext', {}).get('routeKey')
print(routeKey)  # for debugging 

if routeKey != '$connect':  # if we have defined multiple route keys we can choose the right one here
  apigw_management.post_to_connection(ConnectionId=connection_id, Data=ret)
Answered By: LoMaPh

@nemy’s answer is totally true but it doesn’t explain the reason. So, I just want to explain…

So, first of all What is GoneException or GoneError 410 ?

A 410 Gone error occurs when a user tries to access an asset which no longer exists on the requested server. In order for a request to return a 410 Gone status, the resource must also have no forwarding address and be considered to be gone permanently.

you can find more details about GoneException in this article.

In here, GoneException has occured; it means that the POST connection we are trying to make, doesn’t exist – which fits perfectly in the scenario. Because we still haven’t established the connection between Client and Server. The way APIGatewayWebsocketAPIs work is that you request an Endpoint(Route) and that Endpoint will invoke that Lambda Function (In our case it is ConnectionLambdaFunction for $connect Route).

Now, if The Lambda function resolves with statusCode: 200 then and only then the API Gateway will allow the connection to be established. So, basically untill we return statusCode: 200 from our Lambda Function we are not connected and untill then we are totally unknown to server and thats why the Post call that has been made before the return statement itself will throw an error.

Answered By: D_Gamer

When sending a message to a connected client, I can without problem, but when it’s the client ID that -is- connecting via the connect route, it can’t, it shows "goneException" Is that all it shows? Why doesn’t this show me something more specific? Is there anything else I can print?

The above that I said, I understand that it is so, but is it the only thing that I can rescue from the error? Why doesn’t this tell me something more specific like "no connection attempted", but this says "goneException".

Note: My code is in GOOLANG not Python:

if err != nil {
    fmt.Println("WARNING, sendAll(), idconexión: ["+idcliente+"]->", err, "<-")
            if awsErr, ok := err.(awserr.Error); ok {
                log.Println("Error:", awsErr.Code(), awsErr.Message())
                log.Println("Error:", awsErr)
                // Get original error
                fmt.Println("ee: ", errors.Unwrap(awsErr))
                if origErr := errors.Unwrap(awsErr); origErr != nil {
                    fmt.Println(origErr)
                }
    }
}

All errors messages: "goneException" ¿Why not "send not attempt, connectionID does not exist" or similar? In what other "goneException" case, only in this one?

Answered By: motocross