[Interactive Brokers] `generate_fill_reports` sends the prefixed `AccountId` as the execution filter's account code — IB rejects it with error 321
Bug Report
Confirmation
- I've re-read the relevant sections of the documentation.
- I've searched existing issues and discussions to avoid duplicates.
- I've reviewed or skimmed the source code (or examples) to confirm the behavior is not by design.
- I've tested this issue using a recent pre-release or development wheel (
2.0.0rcN,devdevelop, oranightly) and can still reproduce it.
Expected behavior
generate_fill_reports requests executions for the configured account, filtering on the raw IB account code (e.g. U1234567), as every other account-scoped request in the adapter does, and returns that account's historical fill reports during startup reconciliation.
Actual behavior
The execution filter is sent with the Nautilus AccountId string, including the issuer prefix (IB-U1234567). TWS / IB Gateway rejects the request:
[321] Error validating request.-'W' : cause - Invalid account code IB-U1234567.The request never completes, so generate_mass_status gets 0 fill reports. Startup reconciliation still succeeds (positions come from the position reports), so this surfaces only as a warning plus missing historical fills.
In crates/adapters/interactive_brokers/src/execution/core.rs (v2.0.0rc5, 1b0a49d, unchanged on develop):
async fn generate_fill_reports(&self, cmd: GenerateFillReports) -> anyhow::Result<Vec<FillReport>> {
...
// Get account code from account ID
let account_code = self.core.account_id.to_string(); // "IB-U1234567"
...
let filter = ExecutionFilter {
client_id: None,
account_code,
...Every other account-scoped call site strips the prefix with raw_ib_account_code (execution/account.rs:61, 236, 327, 388; execution/core.rs:888, 1178, 1427, 2219). The 1.231.0 Python adapter also used the raw code (execution_filter.acctCode = self.account_id.get_id()), so this looks like a regression from the Rust port. The same line is present in v2.0.0rc3 and v2.0.0rc4.
Suggested fix:
- // Get account code from account ID
- let account_code = self.core.account_id.to_string();
+ let account_code = raw_ib_account_code(&self.core.account_id);Steps to reproduce
- Configure
InteractiveBrokersExecutionClientConfigwithaccount_id="U1234567"(the factory resolves it toAccountId("IB-U1234567")), plus a data client, and build aLiveNodewith.with_reconciliation(True). - Start the node against IB Gateway (live or paper) with that account.
- During
generate_mass_status, the log shows the 321 warning above and0 fill reports.
The rejection is independent of Nautilus. The same request made with ib_async against the same Gateway:
from ib_async import IB, ExecutionFilter
ib = IB(); ib.connect("127.0.0.1", 4001, clientId=97, readonly=True)
ib.RequestTimeout = 10
ib.reqExecutions(ExecutionFilter(acctCode="U1234567")) # -> 11 executions
ib.reqExecutions(ExecutionFilter(acctCode="IB-U1234567")) # -> error 321, times outCode snippets or logs
[INFO] nautilus_live::node: Requesting mass status from IB...
[WARN] nautilus_interactive_brokers::execution::core: Error receiving execution data: [321] Error validating request.-'W' : cause - Invalid account code IB-U1234567.
[INFO] nautilus_interactive_brokers::execution::core: generate_mass_status: 5 order reports, 0 fill reports, 5 position reports
[INFO] nautilus_live::execution::manager: Reconciliation complete for IB: reconciled=0, external=5, open=0, fills=5, positions=0, skipped=0, filtered=0(The login is a financial-advisor login managing several client accounts. Whether TWS validates acctCode the same way for a single-account login has not been checked.)
Specifications
- OS platform: Linux-6.8.0-138-generic-x86_64-with-glibc2.39
- Python version: 3.12.3
nautilus_traderversion: 2.0.0rc5- Installed from (PyPI wheel, package index wheel, or built from source): PyPI wheel
- Adapter/venue (if applicable): Interactive Brokers (IB Gateway, live)
Source: nautechsystems/nautilus_trader