Skip to content
[OPEN_POKER]

Poker Bot को Debug करो: 7 Common WebSocket Errors Fixed

JJoão Carvalho||10 min read

हर bot builder same poker bot WebSocket errors से टकराता है। मैंने सैकड़ों bots को Open Poker से connect होते देखा है, और failure modes remarkably consistent हैं: auth failures, timeouts, malformed JSON, silent disconnects, और race conditions जो सिर्फ load के नीचे दिखते हैं। ये वो सात errors हैं जो तुम्हें hit करेंगी और हर एक को कैसे fix करें। Timeouts पर deeper dive के लिए, Why Your Poker Bot Times Out देखो। अगर तुम अपना पहला bot लिख रहे हो, पहले Python quickstart से शुरू करो।

1. मेरे bot को तुरंत auth_failed क्यों मिलता है?

auth_failed error का मतलब है कि server ने तुम्हारी API key को WebSocket handshake complete होने से पहले reject कर दिया। Socket 4001 code से close होती है, और तुम्हें ये response दिखेगा:

{
  "type": "error",
  "code": "auth_failed",
  "message": "Invalid or missing API key"
}

तीन चीज़ें इसका कारण बनती हैं। सबसे common: missing Authorization header। Python में websockets library custom headers तब तक नहीं भेजती जब तक तुम explicitly pass नहीं करते।

import websockets
 
# गलत: कोई auth header नहीं
ws = await websockets.connect("wss://openpoker.ai/ws")
 
# सही: header explicitly pass करो
headers = {"Authorization": f"Bearer {API_KEY}"}
ws = await websockets.connect("wss://openpoker.ai/ws", additional_headers=headers)

दूसरा कारण: API key में whitespace। अगर तुमने key dashboard या email से copy की, तो trailing newlines घुस जाते हैं। Strip करो: API_KEY = os.environ["POKER_API_KEY"].strip()

तीसरा: तुमने POST /api/me/regenerate-key से key regenerate की और bot config update करना भूल गए। पुरानी key तुरंत invalid हो जाती है। कोई grace period नहीं है।

Full authentication flow के लिए WebSocket protocol docs देखो, जिसमें browser clients के लिए query-parameter fallback भी है।

2. मेरा bot हर hand में auto-fold क्यों करता है?

तुम्हारा bot timeout हो रहा है। Open Poker हर action के लिए 120 seconds देता है। अगर server को उस window में valid action नहीं मिला, तो वो तुम्हारे लिए auto-fold कर देता है। 120 seconds generous लगता है, लेकिन मैंने bots को दो कारणों से इसे blow through करते देखा है।

Event loop को block करना। अगर तुम्हारा your_turn handler synchronous work करता है (HTTP calls, file I/O, heavy computation), तो async for loop stall हो जाता है। Message buffer में बैठा रहता है जबकि तुम्हारा code block रहता है।

# खराब: event loop block करता है
def decide(msg):
    time.sleep(2)  # slow computation simulate कर रहे हैं
    return "call"
 
# अच्छा: async रखो
async def decide(msg):
    await asyncio.sleep(0)  # ज़रूरत हो तो briefly yield करो
    return "call"

your_turn handle नहीं करना। अगर तुम्हारा message router your_turn type string match नहीं करता, तो message silently drop हो जाता है। Unhandled messages के लिए logging add करो:

async for raw in ws:
    msg = json.loads(raw)
    t = msg.get("type")
 
    if t == "your_turn":
        await handle_turn(ws, msg)
    elif t in ("hand_start", "hand_result", "community_cards"):
        pass  # informational
    else:
        print(f"Unhandled message type: {t}")

Bot lifecycle docs हर वो message cover करते हैं जो तुम्हारे bot को handle करना है, exact JSON shapes के साथ।

3. action_rejected errors क्यों आती हैं?

Server ने तुम्हारी action validate की और problem मिली। Timeout से पहले तुम्हें अभी भी valid action भेजना होगा, नहीं तो auto-fold होगा। Response ऐसा दिखता है:

{
  "type": "action_rejected",
  "reason": "Invalid raise amount"
}

Top तीन कारण:

Stale turn_token हर your_turn message में fresh turn_token होता है। तुम्हें वो echo back करना होगा। अगर तुमने पिछले turn से token cache किया (या कभी include नहीं किया), action reject होती है।

# हमेशा CURRENT your_turn का token use करो
await ws.send(json.dumps({
    "type": "action",
    "action": "raise",
    "amount": 60.0,
    "turn_token": msg["turn_token"],  # उस your_turn से जिसका reply कर रहे हो
}))

Raise amount range से बाहर। valid_actions array तुम्हें raises के लिए exact min और max बताता है। उस range से बाहर कुछ भी भेजो और reject हो जाता है। Raise sizes hardcode मत करो।

actions = {a["action"]: a for a in msg["valid_actions"]}
if "raise" in actions:
    min_raise = actions["raise"]["min"]
    max_raise = actions["raise"]["max"]
    # तुम्हारी desired amount, valid range में clamp की हुई
    amount = max(min_raise, min(your_amount, max_raise))

ऐसी action भेजना जो valid नहीं है। अगर valid_actions में सिर्फ fold और call हैं, check भेजना reject होगा। हमेशा array पढ़ो। कभी assume मत करो कि क्या available है।

4. बिना crash के null fields कैसे handle करें?

Open Poker के protocol में कई fields omit होने की बजाय null value के साथ present होती हैं। ये deliberate design choice है (consistent message shapes), लेकिन ये उन bot builders को पकड़ती है जो naive type coercion use करते हैं।

Classic crash:

# जब amount null हो तो TypeError raise करता है
amount = float(msg["amount"])  # float(None) -> TypeError

player_action message folds और checks के लिए amount को null set करती है। Fix straightforward है:

# Safe: null handle करता है
amount = msg.get("amount") or 0.0

to_call_before के लिए same pattern, जो null होता है जब call करने के लिए कुछ नहीं है:

to_call = msg.get("to_call_before") or 0.0

हमने इस bug के एक subtle variant पर करीब 4 घंटे गँवाए। हमारा bot opponent bet sizes track करता था player_action.amount values accumulate करके। जब किसी player ने check किया, null amount ने हमारा running total silently तोड़ दिया। Error तब तक surface नहीं हुई जब तक pot odds calculation ने inf produce नहीं किया। अगर तुम state tracking बना रहे हो, हर message के हर field को validate करो।

Nullable fields की complete list और safe access patterns के लिए message handling docs देखो।

5. मेरा bot disconnect क्यों होता है और seat क्यों खो देता है?

Drop के बाद reconnect करने के लिए तुम्हारे पास 120 seconds हैं। उसके बाद, table से remove हो जाते हो और stack balance में वापस आ जाता है। Server seat hold करता है, लेकिन हमेशा के लिए नहीं।

Common disconnect कारण:

Ping/pong handling नहीं। websockets library ज़्यादातर versions में WebSocket pings automatically handle करती है, लेकिन अगर lower-level client use कर रहे हो या automatic pong responses disable हैं, server idle connections close कर देता है।

Retry logic के बिना network blips। तुम्हारे bot को reconnection wrapper चाहिए:

import asyncio
import json
import websockets
 
async def connect_with_retry(api_key, max_retries=10):
    headers = {"Authorization": f"Bearer {api_key}"}
    retries = 0
    while retries < max_retries:
        try:
            async with websockets.connect(
                "wss://openpoker.ai/ws",
                additional_headers=headers
            ) as ws:
                retries = 0  # successful connection पर reset करो
                await play_loop(ws)
        except (websockets.ConnectionClosed, ConnectionError) as e:
            retries += 1
            wait = min(2 ** retries, 60)  # exponential backoff, 60s पर cap
            print(f"Disconnected: {e}. Retrying in {wait}s ({retries}/{max_retries})")
            await asyncio.sleep(wait)
    print("Max retries reached. Exiting.")

Session takeover। अगर same API key से दूसरा WebSocket खोलते हो, पुरानी connection तुरंत replace हो जाती है। ऐसा होता है जब bot restart करते हो बिना previous process clean die किए। एक agent, एक connection। पहले पुराना process kill करो।

Reconnect करने के बाद, missed events recover करने के लिए resync_request भेजो:

await ws.send(json.dumps({
    "type": "resync_request",
    "table_id": stored_table_id,
    "last_table_seq": last_seq_number
}))

6. rate_limited का मतलब क्या है और इससे कैसे बचें?

Open Poker दो rate limits enforce करता है: 20 messages per second per WebSocket connection, और 10 connection attempts per minute per IP। कोई भी hit करो और तुम्हें ये मिलेगा:

{
  "type": "error",
  "code": "rate_limited",
  "message": "Too many messages per second"
}

Message-per-second limit normal play के दौरान rarely matter करती है। तुम per turn एक action भेजते हो और tables के बीच शायद एक join_lobby। लेकिन वो bots जो server को receive होने वाले हर message को log या echo करते हैं (ऐसा हमने देखा है) उसे जल्दी hit करती हैं।

Connection-attempt limit sneaky वाली है। अगर तुम्हारे reconnection logic में backoff नहीं है, एक network flap seconds में 10 attempts burn कर सकता है, एक minute के लिए lock out करते हुए। ऊपर reconnection example में exponential backoff इसे prevent करता है।

Quick diagnostic: अपने outbound messages count करो। Normal play के दौरान 5 per second से ज़्यादा भेज रहे हो तो कुछ गलत है। संभावना है कि हर received message पर actions re-send कर रहे हो बजाय सिर्फ your_turn पर।

7. मेरा bot lobby join करता है लेकिन कभी seat नहीं मिलती?

ये WebSocket error नहीं है, matchmaking issue है। लेकिन नए builders की सबसे common "मेरा bot broken है" report यही है।

Matchmaker को table बनाने के लिए queue में कम से कम 2 players चाहिए। अगर कोई और नहीं खेल रहा, तुम्हारा bot indefinitely wait करता है। Leaderboard check करो कि दूसरे bots active हैं या नहीं।

अन्य कारण:

SymptomError codeFix
पहले से table पर होalready_seatedपहले leave_table भेजो, फिर lobby rejoin करो
पहले से queued होalready_in_lobbyjoin_lobby दो बार मत भेजो
Active season नहींno_active_seasonअगले season शुरू होने का wait करो
Chips कम हैंinsufficient_season_chipsChip balance check करो

अगर locally test कर रहे हो, दो bot instances अलग-अलग API keys से run करो। ये 2-player minimum पार करने का सबसे fast तरीका है।

जब ऊपर की सातों errors तुम्हारी situation से match नहीं करतीं, ये quick diagnostic sequence run करो: हर raw message print करो (print(f"<< {raw}") अपने async for loop के अंदर), किसी भी error message में code field को error code table से check करो, turn_token verify करो, known-good baseline के रूप में 47-line calling station से test करो, और async handler को block करने वाले किसी time.sleep() या synchronous calls के लिए check करो।

FAQ

मेरा bot locally काम करता है लेकिन production में fail होता है। क्या अलग है? तीन चीज़ें बदलती हैं: URL ws://localhost:8000/ws से wss://openpoker.ai/ws होता है (ध्यान दो wss, ws नहीं), TLS certificate validation kick in करती है, और latency बढ़ती है। अगर locally self-signed cert use कर रहे हो, तुम्हारे production code को real cert chain trust करनी होगी। ज़्यादातर websockets installations ये automatically handle करती हैं, लेकिन custom SSL contexts इसे तोड़ सकते हैं।

मुझे कैसे पता चलेगा कि मेरी action actually accept हुई? Server तुम्हारे client_action_id echoed back के साथ action_ack message भेजता है। अगर client_action_id include नहीं करते, ack में correlation field नहीं मिलेगा। हमेशा एक include करो।

क्या मैं mid-hand reconnect करके फिर भी act कर सकता हूँ? हाँ। तुम्हारे पास 120 seconds हैं। Reconnect करने के बाद, missed events recover करने के लिए table_id और last_table_seq के साथ resync_request भेजो। अगर अभी भी तुम्हारी turn है, current game state के साथ fresh your_turn message मिलेगी।

invalid_message errors क्यों मिलती हैं? तुम्हारा JSON malformed है या required fields missing हैं। Common causes: double quotes की जगह single quotes (Python का json.dumps ये handle करता है, लेकिन f-strings नहीं करतीं), missing type field, या Python dict को JSON में serialize किए बिना directly भेजना।


कोई ऐसा bug है जो यहाँ cover नहीं हुआ? Full protocol reference पढ़ो या अपना bot register करो और live server के against debug करना शुरू करो। Protocol सीखने का सबसे अच्छा तरीका है हर message print करो और उन्हें पढ़ो।

और पढ़ो