Python IB-API实时行情获取报错:EReader线程未处理异常(WinError10038)
I’ve run into this exact issue before—let’s break down what’s happening and how to fix it.
Why This Error Happens
The WinError 10038 and "unhandled exception in EReader thread" error pops up because:
- When you call
app.disconnect()at the end of your script, the IB API client closes the network socket immediately. - But the EReader thread (the one you started with
self.run()inTestApp.__init__) is still running in the background. It keeps trying to callrecv()to read data from the now-closed socket, which isn’t a valid socket anymore—hence the error. - The error doesn’t break your data fetching because it happens after you’ve already retrieved all the market data you need. It’s just a messy, ungraceful exit.
Step-by-Step Fixes
1. Add Graceful Thread Cleanup to Your Main Function
The simplest fix is to wait for the EReader thread to exit after disconnecting. Modify your main function like this:
def main(slist): app = TestApp("127.0.0.1", 7497, 1) try: for i in slist: ibcontract = IBcontract() ibcontract.secType = "STK" ibcontract.symbol = str(i) # Important: IB expects symbol as a string, not integer ibcontract.exchange ="SEHK" Lastprice = app.getIBrealtimedata(ibcontract) df = pd.DataFrame(Lastprice) print(ibcontract.symbol, df.head()) finally: # Disconnect first, then wait for the EReader thread to shut down app.disconnect() app._thread.join(timeout=2) # Give it 2 seconds to exit gracefully
Note: I also added str(i) for the symbol—IB’s API requires the symbol to be a string, and passing integers could cause silent issues down the line.
2. Override the Run Logic for More Control (More Robust)
For a cleaner, more permanent fix, modify your TestApp class to include a stop flag, so the EReader thread knows when to exit:
class TestApp(TestWrapper, TestClient): def __init__(self, ipaddress, portid, clientid): TestWrapper.__init__(self) TestClient.__init__(self, wrapper=self) self.connect(ipaddress, portid, clientid) self._stop_thread = False # Flag to signal thread exit thread = Thread(target=self.run_loop) thread.start() setattr(self, "_thread", thread) self.init_error() def run_loop(self): # Run the EReader loop only while connected and not stopped while not self._stop_thread and self.isConnected(): try: self.run() except Exception as e: print(f"EReader thread exited with: {e}") break def disconnect(self): # Signal thread to stop before disconnecting self._stop_thread = True super().disconnect() self._thread.join(timeout=3) # Wait for thread to finish
This way, when you call disconnect(), the thread gets a clear signal to stop trying to read from the socket, avoiding the error entirely.
3. Minor Tweak to Data Fetching
In your getIBrealtimedata method, adding a tiny delay after canceling market data can help ensure the IB server processes the cancel request before you move on:
def getIBrealtimedata(self, ibcontract, tickerid=DEFAULT_PRICE_DATA_ID): ib_data_queue = finishableQueue(self.init_ibprices(tickerid)) self.reqMktData( tickerid, ibcontract, "", False, False, [] ) MAX_WAIT_SECONDS = 5 print("Getting data from the server... could take %d seconds to complete " % MAX_WAIT_SECONDS) price_data = ib_data_queue.get(timeout = MAX_WAIT_SECONDS) while self.wrapper.is_error(): print(self.get_error()) if ib_data_queue.timed_out(): print("Exceeded maximum wait for wrapper to confirm finished - seems to be normal behaviour") self.cancelMktData(tickerid) import time time.sleep(0.1) # Tiny delay to let IB process the cancel return price_data
This isn’t strictly necessary, but it reduces the chance of leftover socket activity causing issues.
内容的提问来源于stack exchange,提问作者Michael Siu

