You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Metaculus exchange constructor uses different credential parameter names in each SDK for the same bearer-token concept. Python uses api_token; TypeScript uses the shared apiKey field from ExchangeOptions.
TypeScript SDK
sdks/typescript/pmxt/client.ts, line 2574 — Metaculus constructor accepts the standard ExchangeOptions interface which provides apiKey:
// sdks/typescript/pmxt/client.ts line 2574exportclassMetaculusextendsExchange{constructor(options: ExchangeOptions={}){super({ ...options,exchange: 'metaculus'});}}
ExchangeOptions.apiKey (models.ts line 169) is the field through which an API key credential is passed to all exchanges.
Exchange.__init__ then maps api_token into _get_credentials_dict and sends it to the sidecar as apiToken (client.py line 263).
Expected
Both SDKs should use the same credential parameter name for Metaculus. If Metaculus requires a bearer token semantically distinct from a generic API key, both SDKs should expose the same parameter name (e.g., both api_token / apiToken, or both api_key / apiKey). The sidecar field name sent over the wire should also be consistent.
Impact
Users migrating between SDKs or reading cross-SDK docs will pass api_key in Python (following the TypeScript pattern) and receive no authentication error — the credential will be silently dropped and requests will fail or be unauthenticated.
return{apiKey: this.apiKey,// becomes credentials.apiKey on server — factory ignores it// credentials.apiToken is never populated};
A TS SDK user doing new Metaculus({ apiKey: 'my-token' }) sends credentials.apiKey to the server. The factory reads credentials.apiToken, finds it undefined, and falls back to process.env.METACULUS_API_TOKEN. If that env var is unset, the exchange initialises unauthenticated even though the user explicitly provided a token.
Fix: add an apiToken field to ExchangeOptions (or a MetaculusOptions subtype) and include it in getCredentials().
Additional context from duplicate #469: base-class detail — sdks/python/pmxt/client.py line 263, Exchange.__init__ has api_token: Optional[str] = None, forwarded via _get_credentials_dict() as "apiToken" to the sidecar; TypeScript ExchangeOptions (client.ts lines 167-213) has no apiToken field at all. #469 also proposes an alternate fix worth noting: remove api_token from the Python base Exchange class entirely and scope it only to the Metaculus subclass in both SDKs (which would converge with this issue's framing).
Additional context from duplicate #1585: a deeper audit confirms this is worse than a naming mismatch. grep -n "apiToken" sdks/typescript/pmxt/client.ts returns zero matches — the TypeScript SDK has no apiToken field anywhere (not in ExchangeOptions, not in Exchange.getCredentials(), no override on the Metaculus subclass). apiKey is not a working substitute: Metaculus authenticates via a bearer-style Authorization: Token <token> header read from credentials.apiToken in core (core/src/exchanges/metaculus/index.ts:65,93-111), which the TS SDK never populates. sdks/typescript/API_REFERENCE.md:2037 already documents apiToken: string as part of ExchangeCredentials — the docs describe a field that was never wired into client.ts. Practical impact: every authenticated Metaculus call via the TS SDK (createOrder, cancelOrder, higher-rate-limit reads) fails with AuthenticationError, with no way to supply a token — this is a functional blocker (P1), not a cosmetic naming asymmetry. Python's implementation is correct (sdks/python/pmxt/_exchanges.py:341,357,564). There's an open fix PR: #2018.
Activity
realfishsam commentedon Jun 6, 2026
Additional context from duplicate #882: the runtime consequence of this naming mismatch is that TS SDK auth silently breaks. Specific evidence:
Core factory (
core/src/server/exchange-factory.ts:100–104):TS SDK
getCredentials()(sdks/typescript/pmxt/client.ts:359–368):A TS SDK user doing
new Metaculus({ apiKey: 'my-token' })sendscredentials.apiKeyto the server. The factory readscredentials.apiToken, finds itundefined, and falls back toprocess.env.METACULUS_API_TOKEN. If that env var is unset, the exchange initialises unauthenticated even though the user explicitly provided a token.Fix: add an
apiTokenfield toExchangeOptions(or aMetaculusOptionssubtype) and include it ingetCredentials().Generated by Claude Code
Metaculussends auth incredentials.apiKeybut core factory readscredentials.apiToken#882 as a duplicate of this issuerealfishsam commentedon Jul 3, 2026
Additional context from duplicate #469: base-class detail —
sdks/python/pmxt/client.pyline 263,Exchange.__init__hasapi_token: Optional[str] = None, forwarded via_get_credentials_dict()as"apiToken"to the sidecar; TypeScriptExchangeOptions(client.tslines 167-213) has noapiTokenfield at all. #469 also proposes an alternate fix worth noting: removeapi_tokenfrom the Python baseExchangeclass entirely and scope it only to theMetaculussubclass in both SDKs (which would converge with this issue's framing).Generated by Claude Code
Exchange.__init__hasapi_tokenparameter not present in TypeScriptExchangeOptions#469 as a duplicate of this issuerealfishsam commentedon Aug 18, 2026
Additional context from duplicate #1585: a deeper audit confirms this is worse than a naming mismatch.
grep -n "apiToken" sdks/typescript/pmxt/client.tsreturns zero matches — the TypeScript SDK has noapiTokenfield anywhere (not inExchangeOptions, not inExchange.getCredentials(), no override on theMetaculussubclass).apiKeyis not a working substitute: Metaculus authenticates via a bearer-styleAuthorization: Token <token>header read fromcredentials.apiTokenin core (core/src/exchanges/metaculus/index.ts:65,93-111), which the TS SDK never populates.sdks/typescript/API_REFERENCE.md:2037already documentsapiToken: stringas part ofExchangeCredentials— the docs describe a field that was never wired intoclient.ts. Practical impact: every authenticated Metaculus call via the TS SDK (createOrder,cancelOrder, higher-rate-limit reads) fails withAuthenticationError, with no way to supply a token — this is a functional blocker (P1), not a cosmetic naming asymmetry. Python's implementation is correct (sdks/python/pmxt/_exchanges.py:341,357,564). There's an open fix PR: #2018.Generated by Claude Code