Skip to content

SDK drift: Python Metaculus uses api_token credential param; TypeScript uses apiKey #465

Open

Description

@realfishsam

Drift

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 2574
export class Metaculus extends Exchange {
    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.

Python SDK

sdks/python/pmxt/_exchanges.py, lines 309–320 — Metaculus constructor accepts a dedicated api_token parameter instead:

# sdks/python/pmxt/_exchanges.py lines 309–320
class Metaculus(Exchange):
    def __init__(
        self,
        api_token: Optional[str] = None,
        pmxt_api_key: Optional[str] = None,
        base_url: Optional[str] = None,
        **kwargs,
    ):
        super().__init__(
            exchange="metaculus",
            api_token=api_token,
            ...
        )

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.

Found by automated SDK cross-language drift audit

Activity

  1. realfishsam commented on Jun 6, 2026

    @realfishsam
    ContributorAuthor

    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):

    case "metaculus":
      return new MetaculusExchange({
        apiToken: credentials?.apiToken || process.env.METACULUS_API_TOKEN,
        // No reference to credentials.apiKey
      });

    TS SDK getCredentials() (sdks/typescript/pmxt/client.ts:359–368):

    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().


    Generated by Claude Code

  2. realfishsam commented on Jul 3, 2026

    @realfishsam
    ContributorAuthor

    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).


    Generated by Claude Code

  3. realfishsam commented on Aug 18, 2026

    @realfishsam
    ContributorAuthor

    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.


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions