Skip to content
This repository was archived by the owner on Apr 26, 2026. It is now read-only.

Commit 9ecea97

Browse files
Copilottheshadow76
andcommitted
Add comprehensive SSID validation and improve authentication error messages
Co-authored-by: theshadow76 <59869868+theshadow76@users.noreply.github.com>
1 parent 067d8c0 commit 9ecea97

3 files changed

Lines changed: 124 additions & 20 deletions

File tree

README.md

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,9 +73,39 @@ Example SSID format:
7373

7474
If you are unable to find it, try running the automatic SSID scraper under the `tools` folder.
7575

76-
## Comon errors
76+
## Common errors
7777

78-
### Traceback:
78+
### Authentication timeout or connection immediately closes
79+
80+
If you see errors like:
81+
```
82+
WARNING | pocketoptionapi_async.websocket_client:receive_messages:395 - WebSocket connection closed
83+
WARNING | pocketoptionapi_async.client:_start_regular_connection:245 - Failed to connect to region DEMO: Authentication timeout
84+
```
85+
86+
**Solution**: Your SSID is likely in the wrong format or is expired. Make sure you are using the **complete SSID format**, not just the session ID:
87+
88+
**Correct format:**
89+
```python
90+
SSID = '42["auth",{"session":"n1p5ah5u8t9438rbunpgrq0hlq","isDemo":1,"uid":84402008,"platform":1}]'
91+
```
92+
93+
**Wrong format (just the session):**
94+
```python
95+
SSID = 'dxxxxxxxxxxxxxxxxxxxxxxxxxxxx' # This won't work!
96+
```
97+
98+
To get the correct SSID:
99+
1. Open PocketOption in your browser
100+
2. Open Developer Tools (F12)
101+
3. Go to Network tab
102+
4. Filter by "WS" (WebSocket)
103+
5. Look for a message that starts with `42["auth",`
104+
6. Copy the **entire message** including the `42["auth",{...}]` part
105+
106+
### Websockets version error
107+
108+
## Traceback:
79109
```
80110
2025-07-13 15:25:16.531 | INFO | pocketoptionapi_async.client:__init__:130 - Initialized PocketOption client (demo=True, uid=105754921, persistent=False) with enhanced monitoring
81111
2025-07-13 15:25:16.532 | INFO | pocketoptionapi_async.client:connect:162 - Connecting to PocketOption...

pocketoptionapi_async/client.py

Lines changed: 82 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -78,14 +78,10 @@ def __init__(
7878
if not enable_logging:
7979
logger.remove()
8080
logger.add(lambda msg: None, level="CRITICAL") # Disable most logging
81-
# Parse SSID if it's a complete auth message
81+
82+
# Validate and parse SSID
8283
self._original_demo = None # Store original demo value from SSID
83-
if ssid.startswith('42["auth",'):
84-
self._parse_complete_ssid(ssid)
85-
else:
86-
# Treat as raw session ID
87-
self.session_id = ssid
88-
self._complete_ssid = None
84+
self._validate_and_parse_ssid(ssid)
8985

9086
# Core components
9187
self._websocket = AsyncWebSocketClient()
@@ -656,6 +652,39 @@ def get_connection_stats(self) -> Dict[str, Any]:
656652

657653
return stats # Private methods
658654

655+
def _validate_and_parse_ssid(self, ssid: str) -> None:
656+
"""Validate and parse SSID format"""
657+
if not ssid or not isinstance(ssid, str):
658+
raise InvalidParameterError(
659+
"SSID must be a non-empty string. "
660+
"Expected format: 42[\"auth\",{\"session\":\"...\",\"isDemo\":1,\"uid\":0,\"platform\":1}]"
661+
)
662+
663+
ssid = ssid.strip()
664+
665+
# Check if it's a complete SSID format
666+
if ssid.startswith('42["auth",'):
667+
self._parse_complete_ssid(ssid)
668+
# Validate that we got a session ID
669+
if not self.session_id or len(self.session_id) < 10:
670+
raise InvalidParameterError(
671+
f"Invalid SSID format - session ID is too short or missing. "
672+
f"Please ensure your SSID is in the correct format: "
673+
f"42[\"auth\",{{\"session\":\"your_session_id\",\"isDemo\":1,\"uid\":12345,\"platform\":1}}]. "
674+
f"You can get this from browser DevTools (F12) -> Network tab -> WS filter -> "
675+
f"look for authentication message starting with 42[\"auth\","
676+
)
677+
else:
678+
# Treat as raw session ID
679+
if len(ssid) < 10:
680+
logger.warning(
681+
f"Raw session ID appears to be too short ({len(ssid)} chars). "
682+
f"If you're having connection issues, please use the complete SSID format: "
683+
f"42[\"auth\",{{\"session\":\"your_session\",\"isDemo\":1,\"uid\":12345,\"platform\":1}}]"
684+
)
685+
self.session_id = ssid
686+
self._complete_ssid = None
687+
659688
def _format_session_message(self) -> str:
660689
"""Format session authentication message"""
661690
# Always create auth message from components using constructor parameters
@@ -683,41 +712,80 @@ def _parse_complete_ssid(self, ssid: str) -> None:
683712
data = json.loads(json_part)
684713

685714
self.session_id = data.get("session", "")
715+
if not self.session_id:
716+
raise InvalidParameterError(
717+
"SSID is missing the 'session' field. "
718+
"Expected format: 42[\"auth\",{\"session\":\"your_session\",\"isDemo\":1,\"uid\":12345,\"platform\":1}]"
719+
)
720+
686721
# Store original demo value from SSID, but don't override the constructor parameter
687722
self._original_demo = bool(data.get("isDemo", 1))
688723
# Keep the is_demo value from constructor - don't override it
689724
self.uid = data.get("uid", 0)
690725
self.platform = data.get("platform", 1)
691726
# Don't store complete SSID - we'll reconstruct it with correct demo value
692727
self._complete_ssid = None
728+
else:
729+
raise InvalidParameterError(
730+
"Could not parse SSID - JSON object not found. "
731+
"Expected format: 42[\"auth\",{\"session\":\"your_session\",\"isDemo\":1,\"uid\":12345,\"platform\":1}]"
732+
)
733+
except json.JSONDecodeError as e:
734+
raise InvalidParameterError(
735+
f"Invalid SSID format - JSON parsing failed: {e}. "
736+
f"Expected format: 42[\"auth\",{{\"session\":\"your_session\",\"isDemo\":1,\"uid\":12345,\"platform\":1}}]"
737+
)
738+
except InvalidParameterError:
739+
raise # Re-raise our custom errors
693740
except Exception as e:
694-
logger.warning(f"Failed to parse SSID: {e}")
695-
self.session_id = ssid
696-
self._complete_ssid = None
741+
raise InvalidParameterError(
742+
f"Failed to parse SSID: {e}. "
743+
f"Expected format: 42[\"auth\",{{\"session\":\"your_session\",\"isDemo\":1,\"uid\":12345,\"platform\":1}}]"
744+
)
697745

698746
async def _wait_for_authentication(self, timeout: float = 10.0) -> None:
699747
"""Wait for authentication to complete (like old API)"""
700748
auth_received = False
749+
auth_error = None
701750

702751
def on_auth(data):
703752
nonlocal auth_received
704753
auth_received = True
754+
755+
def on_auth_error(data):
756+
nonlocal auth_error
757+
auth_error = data.get("message", "Unknown authentication error")
705758

706-
# Add temporary handler
759+
# Add temporary handlers
707760
self._websocket.add_event_handler("authenticated", on_auth)
761+
self._websocket.add_event_handler("auth_error", on_auth_error)
708762

709763
try:
710764
# Wait for authentication
711765
start_time = time.time()
712-
while not auth_received and (time.time() - start_time) < timeout:
766+
while not auth_received and not auth_error and (time.time() - start_time) < timeout:
713767
await asyncio.sleep(0.1)
714768

769+
if auth_error:
770+
raise AuthenticationError(
771+
f"Authentication failed: {auth_error}. "
772+
f"Please verify your SSID is correct. "
773+
f"SSID should be in format: 42[\"auth\",{{\"session\":\"your_session\",\"isDemo\":1,\"uid\":12345,\"platform\":1}}]. "
774+
f"Get it from browser DevTools (F12) -> Network tab -> WS filter -> look for message starting with 42[\"auth\","
775+
)
776+
715777
if not auth_received:
716-
raise AuthenticationError("Authentication timeout")
778+
raise AuthenticationError(
779+
"Authentication timeout - server did not respond to authentication request. "
780+
"This usually means your SSID is invalid or expired. "
781+
"Please get a fresh SSID from browser DevTools (F12) -> Network tab -> WS filter -> "
782+
"look for authentication message starting with 42[\"auth\",{\"session\":\"...\",..."
783+
)
717784

718785
finally:
719-
# Remove temporary handler
786+
# Remove temporary handlers
720787
self._websocket.remove_event_handler("authenticated", on_auth)
788+
self._websocket.remove_event_handler("auth_error", on_auth_error)
721789

722790
async def _initialize_data(self) -> None:
723791
"""Initialize client data after connection"""

pocketoptionapi_async/websocket_client.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -582,8 +582,11 @@ async def _process_message(self, message) -> None:
582582
await self._handle_json_message(data)
583583

584584
elif message.startswith("42") and "NotAuthorized" in message:
585-
logger.error("Authentication failed: Invalid SSID")
586-
await self._emit_event("auth_error", {"message": "Invalid SSID"})
585+
logger.error(
586+
"Authentication failed: Server rejected SSID. "
587+
"Please verify your SSID is correct and not expired."
588+
)
589+
await self._emit_event("auth_error", {"message": "Invalid or expired SSID - Server returned NotAuthorized"})
587590

588591
except Exception as e:
589592
logger.error(f"Error processing message: {e}")
@@ -611,8 +614,11 @@ async def _handle_json_message_wrapper(self, message: str) -> None:
611614
async def _handle_auth_message(self, message: str) -> None:
612615
"""Handle authentication message"""
613616
if "NotAuthorized" in message:
614-
logger.error("Authentication failed: Invalid SSID")
615-
await self._emit_event("auth_error", {"message": "Invalid SSID"})
617+
logger.error(
618+
"Authentication failed: Server rejected SSID. "
619+
"Please verify your SSID is correct and not expired."
620+
)
621+
await self._emit_event("auth_error", {"message": "Invalid or expired SSID - Server returned NotAuthorized"})
616622

617623
async def _process_message_optimized(self, message) -> None:
618624
"""

0 commit comments

Comments
 (0)