function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
Ben EdwardsBen Edwards 

“403::Unknown client” Error When Connecting to Streaming API Push Topic

I am trying to connect to a Push Topic via the Salesforce Streaming API using Python. My application completes the initial handshake correctly, but then fails on the following connect call, with the error: "403::Unknown client".

My messages work as follows:

Handshake Request
{
    "channel":"/meta/handshake",
    "id":"1",
    "supportedConnectionTypes":["long-polling"],
    "version":"1.0",
    "minimumVersion":"1.0"
}
Handshake Response
{
    "channel":"/meta/handshake",
    "clientId":"xxx",
    "version":"1.0",
    "successful":true,
    "minimumVersion":"1.0",
    "id":"1",
    "supportedConnectionTypes":["long-polling"]
}
It's completed the handshake fine. And then...
Connect Request
{
    "channel":"/meta/connect",
    "clientId":"xxx",
    "id":"2",            
    "connectionType":"long-polling"
}
Connect Response
{
    "channel":"/meta/connect",
    "clientId":"xxx",
    "advice":{
        "reconnect":"handshake",
        "interval":500
    },
    "error":"403::Unknown client",
    "successful":false,
    "id":"2"
}
I have seen this issue here:
https://success.salesforce.com/issues_view?id=a1p30000000T0F0AAK

And read through the documentation, including:
http://www.salesforce.com/developer/docs/api_streaming/Content/DebuggingStreamingAPIApplications.htm

After reading this however, I am no closer to determining what the actual fault or problem is, or how to remedy it.

I have tried a lot of different things and am at a loss at how to get it to work. I am using this Bayeux client library https://github.com/dkmadigan/python-bayeux-client, with some custom amendments to get the authentication working.

Any help would be greatly appreciated.
ShashankShashank (Salesforce Developers) 
Did you get a chance to try the workaround mentioned in the known issue page?
Jericho YuenJericho Yuen
Hi Ben, I tried to use the same Bayeux client library and encountered the same 403 error.
Did you resolve this issue? Any help will be greatly appreciated.
Nathan PilkingtonNathan Pilkington
In case anybody else finds this thread like I did, searching for the error message:

I encountered the same error message and the solution in my case was to use the correct header value for the OAuth token. The rest API uses "Authorization: Bearer <Token>" so I was using that for connecting to the streaming API also. Turns out the streaming API uses "Authorization: OAuth <Token>" Instead. I switched it over to "Authorization: OAuth <Token>" and it started working
sumit mishra 5sumit mishra 5

For those who are still struck at this issue-> This issue results wheneva cometd is not configured properly or the libraries are not loaded properly.

If you configuration is correct, try the handshake once all libraries are loaded fully.

 

var auth = 'OAuth ' + token;
            var cometdURL = window.location.protocol + '//' +
                window.location.hostname + (null != window.location
                    .port ? (':' + window.location.port) : '') +
                '/cometd/40.0/';
            setTimeout(function() {
              initialize();
            }, 2000);

            

            function initialize() {
                cometd.configure({
                    url: cometdURL,
                    requestHeaders: {
                        Authorization: auth
                    },
                    appendMessageTypeToURL: false,
                    stickyReconnect: false,
                    logLevel: "info"
                });

                cometd.addListener('/meta/connect',
                    _metaConnect);

                cometd.addListener('/meta/handshake',
                    metaHandshakeListener);

                cometd.addListener('/meta/disconnect',
                    metaDisconnectListener);

                cometd.addListener('/meta/subscribe',
                    metaSubscribeListener);

                cometd.addListener('/meta/unsubscribe',
                    metaUnSubscribeListener);

                cometd.addListener('/meta/unsuccessful',
                    metaUnSucessfulListener);
                try {
                    cometd.handshake();
                } catch (e) {
Rick YangRick Yang
@sumit mishra 5

setTimeout(function() {
}, 2000);

Delay solves my issue. LIke Sumit said, I guess it is because the libraries are not loaded properly. But how can I know 2000 is enough for complete loading?
 
PatMcClellan__cPatMcClellan__c
Instead of using a timeout, just put the CometD subscription handler in afterScriptsLoaded.
<ltng:require scripts="{!$Resource.cometd}" afterScriptsLoaded="{!c.onCometdLoaded}" />

 
Stephanie Cory 30Stephanie Cory 30
I have my code set up like you suggest (to run afterScriptsLoaded) PatMcClellan__c, but I continue to get 403 errors every few seconds.  I handle the error by re-subscribing to my topics and this works fine, but the long polling doesn't restart after the 403 error.  Any suggestions?
PatMcClellan__cPatMcClellan__c
Hi Stephanie, I don't have a solution, but I can commiserate! I'm having the same issue and trying to figure out the best way to mitigate it. About half the time, it won't establish a connection. Then, the other half, it will establish the connection but will fail after the first successful execution of it. About 1 in 50 tries, it establishes a robust connection that reestablishes consistently after each transaction.

Please post here if you figure out something, and I'll do the same. 
otv20otv20
Hi guys,

I'm having the same issue with subscriptions, I can't figure it out yet how to make it work.
I'll keep You post on this as well.

Regards.
Stephanie Cory 30Stephanie Cory 30
I was able to speak with one of the developers on the Streaming API team.  We found that my issue is due to lightning notifications also utilizing cometD and that multiple clients are not currently allowed.  When it notices that there are 2 clients (1 is my app, 1 is the notifications platform), it kills the app client.  Easiest workaround for me was to turn off notifications (Setup>Apps>Mobile Apps>Salesforce>Salesforce Notifications> Uncheck All), but this will get rid of the notification bell in the top right corner.  Next option is to wait for the next release which (Safe Harbor) should have a big update to the streaming API. I'm going to continue digging to see if there is a way to have both features enabled - if I find anything I will let you know.
PatMcClellan__cPatMcClellan__c
Stephanie... THANK YOU. That is an immediate fix to my app. Now it is rock-solid! That gives me assurance that my code is right... now, as you say, we need to figure out how to make it work without disabling Notifications.
otv20otv20
It worked for me as well!

Thanks
sumit mishra 5sumit mishra 5

For those who continue to face error 403, there's a fix that worked for me. its mentioned below

- whenever 403 error occurs you will be getting connection broken. SO do one thing add listener metaUnSucessfulListener.
Once you recieve the error, the method will get called and in this method first unsubsicribe the existing insance of cometd, create fresh instance.

 function unsubscribe() {
           
                cometd.disconnect(true);

            }
 function metaUnSucessfulListener(message) {
                console.log(
                    'DEBUG:  /meta/unsuccessful Error: ' +
                    JSON.stringify(message) + ' <br>');
                    unsubscribe();
                    setTimeout( function(){
                        console.log('init of pt failed');
                        
                        
                        initialize();},10000);
            };
function initialize() {
                cometd = new dojox.CometD();
                 cometd.websocketEnabled = false;
                 token = '{!$Api.Session_ID}';
                auth = 'OAuth ' + token;
                cometd.configure({
                    url: cometdURL,
                    requestHeaders: {
                        Authorization: auth
                    },
                    appendMessageTypeToURL: false,
                    stickyReconnect: false,
                    logLevel: "info"
                });

                cometd.addListener('/meta/connect',
                    _metaConnect);

                cometd.addListener('/meta/handshake',
                    metaHandshakeListener);

                cometd.addListener('/meta/disconnect',
                    metaDisconnectListener);

                cometd.addListener('/meta/subscribe',
                    metaSubscribeListener);

                cometd.addListener('/meta/unsubscribe',
                    metaUnSubscribeListener);

                cometd.addListener('/meta/unsuccessful',
                    metaUnSucessfulListener);
                try {
                    cometd.handshake();
                } catch (e) {
                     cometd.handshake();
                    console.log(e);

                }
            }

 

toby s 1toby s 1
 None of this works for me. I am able to handshake and then when it tries to connect it fails exactly as the OP. I have tried all workarounds without success
PatMcClellan__cPatMcClellan__c
See Stephanie Cory's post above. In Lightning Experience, in-app notifications are enabled by default and they use the 1 available CometD channel. If you want to use Streaming API (which uses CometD), you have to turn off in-app notifications and push notifications. They're working on an overhaul, but it won't be before Winter '19 at least.
toby s 1toby s 1

Thanks Pat. I did try that to no avail. For completeness, heres my requests/responses:

handshake:

req:
[  
   {  
      "channel":"/meta/handshake",
      "supportedConnectionTypes":[  
         "long-polling"
      ],
      "version":"1.0",
      "id":"2"
   }
]

response:
[  
   {  
      "ext":{  
         "replay":true,
         "payload.format":true
      },
      "minimumVersion":"1.0",
      "clientId":"3j91sicmk06mia9pp9cnsgi39ahs",
      "supportedConnectionTypes":[  
         "long-polling"
      ],
      "channel":"/meta/handshake",
      "id":"2",
      "version":"1.0",
      "successful":true
   }
]

Connect:

request
[  
   {  
      "channel":"/meta/connect",
      "connectionType":"long-polling",
      "advice":{  
         "timeout":0
      },
      "id":"3",
      "clientId":"3j91sicmk06mia9pp9cnsgi39ahs"
   }
]

response:
[  
   {  
      "advice":{  
         "interval":0,
         "reconnect":"handshake"
      },
      "channel":"/meta/connect",
      "id":"3",
      "error":"403::Unknown client",
      "successful":false
   }
]

PatMcClellan__cPatMcClellan__c
@Toby s 1, I've got it working for me, but it was largely through pulling code and methods from the CometD documentation, not through a thorough understanding -- I'm not expert enough to spot any errors in your code. Here's the JS controller I'm using -- including lots of console.log statements to help track what's going on. (I use lots of emojis in my console.log and system.debug statements to help me find things quickly in the logs.)  This is the controller for a component that is nothing more than an indicator "light" that turns green when v.Connected is true, or red when it's not.
 
({
    connectCometd : function(component, event, helper)
    {
        var _connected = false;
        // functions that manage connection status with Bayeux server
        
        function _metaConnect(message)
        {
            if (cometd.isDisconnected())
            {
                _connected = false;
                console.log("❗ CometD connection closed.");
                component.set("v.Connected", false);
                return;
            }

            var wasConnected = _connected;
            _connected = message.successful === true;
            if(!wasConnected && _connected){
                console.log("✅ CometD connection established.");
               component.set("v.Connected", true);
            }else if (!wasConnected && !_connected)
            {
                console.log("❗ CometD connection broken.");
                component.set("v.Connected", false);
            }
        }

        function _metaUnsuccessfulListener(message){
            console.log('DEBUG: /meta/unsuccessful Error: ' + JSON.stringify(message)+ '<br>');
            helper.disconnectCometd(component);
            setTimeout(function(){
                console.log('Init of CometD failed');
                try{
                    cometd.handshake();
                }catch(e){
                    console.log('_metaUnsuccessful was unsuccessful in reestablishing connection: ' + e);
                }
            }, 5000);
        }

        // _metaHandshake makes subscribe conditional on successful handshake
        function _metaHandshake(handshake)
        {
            if(handshake.successful)
            {
                console.log('🤝 Handshake successful, clientId: '+ handshake.clientId);
                
                // Subscribe to pushTopic
                var newSubscription = cometd.subscribe("/topic/MyTopicReceived",
                    function(pushTopic)
                    {
                        console.log('My topic received, Parameters: '+ pushTopic.data.sobject[ParamField]);
                        var appEvent = $A.get("e.c:TopicReceived");
                        appEvent.setParams({
                            ObjectId: pushTopic.data.sobject[ParamField]
                        }).fire();
                        console.log("🔥🔥Received PushTopic, fired Application Event: " + pushTopic.data.sobject[ParamField]);
                    });
                var subscriptions = component.get("v.cometdSubscriptions");
                subscriptions.push(newSubscription);
                component.set("v.cometSubscriptions", subscriptions);
            }
            else
                console.error("❗ CometD handshake error: " + handshake.error);
        }

        // Configure CometD


        var cometdUrl = '/cometd/42.0/';
        var cometd = component.get('v.cometd');
        cometd.websocketEnabled = false;
        cometd.configure({
            url: cometdUrl,
            requestHeaders: { Authorization: "OAuth " + component.get("v.sessionId")},
            appendMessageTypeToURL : false
        });

        //add listeners
        cometd.addListener('/meta/handshake', _metaHandshake);
        cometd.addListener('/meta/connect', _metaConnect);
        cometd.addListener('/meta/unsuccessful', _metaUnsuccessfulListener);
        cometd.addListener('/meta/disconnect', function(message){
            console.log('disconnect: ' +  JSON.stringify(message));
        });
        cometd.addListener('/meta/subscribe', function(message){
            console.log('subscribe: ' + JSON.stringify(message));
            console.log('Subscriptions: ' + JSON.stringify(component.get("v.cometdSubscriptions")));
        });
        cometd.addListener('/meta/unsubscribe', function(message){
            console.log('unsubscribe: ' +  JSON.stringify(message));
        });

        try{
            cometd.handshake();
        }catch(e){
            cometd.handshake();
            console.log(e);
        }
    },

    disconnectCometd : function(component)
    {
        var cometd = component.get('v.cometd');

        // Unsuscribe all CometD subscriptions
        cometd.batch(function() {
            var subscriptions = component.get('v.cometdSubscriptions');
            subscriptions.forEach(function (subscription) {
            cometd.unsubscribe(subscription);
            });
        });
        component.set('v.cometdSubscriptions', []);

        // Disconnect CometD
        cometd.disconnect();
        console.log('🐝 CometD disconnected.');
    },
})
Then on the server-side, you have your APEX class:
 
public with sharing class CometDgetSessionId {
    @AuraEnabled
    public static String getUserSessionId() {
        return UserInfo.getSessionId();
    }


    @AuraEnabled
    public static String[] setupPushTopic() {

     // push topic setup code

   }

}

Hope that helps.
 
Puneet Gosain 35Puneet Gosain 35
Hi, we implemented a client for connecting to a Push Topic via the Salesforce Streaming API in Java, using as example the EMP-Connector. 
The application completes the initial handshake correctly, and everytime it received a "403::Unknown client" message on /meta/connect it re-handshake correctly:

07:04:46.528 Received messages [{clientId=xxx, channel=/meta/connect, id=664, successful=true}]
07:04:46.558 Received messages [{advice={reconnect=handshake, interval=0}, channel=/meta/connect, id=665, error=403::Unknown client, successful=false}]
07:04:46.587 Received messages [{ext={replay=true, payload.format=true}, minimumVersion=1.0, clientId=xxx, supportedConnectionTypes=[Ljava.lang.Object;@565cfe39, channel=/meta/handshake, id=666, version=1.0, successful=true}]
07:04:46.610 Received messages [{clientId=xxx, advice={reconnect=retry, interval=0, timeout=110000}, channel=/meta/connect, id=667, successful=true}]
07:06:36.635 Received messages [{clientId=xxx, channel=/meta/connect, id=668, successful=true}]

The problem is after the second handshake, no event is received from other channel. According to the log above, it seems that the reconnection works fine. Any ideas?