Skip to content

Probable implicit-API path-collision makes GET /events/ and GET /markets/ list endpoints unreachable via callApi() #1902

Description

@realfishsam

Gap

ProbableExchange registers its implicit API from core/src/exchanges/probable/api.ts via defineImplicitApi(parseOpenApiSpec(probableApiSpec, ...)) (core/src/exchanges/probable/index.ts:72-73). Like Myriad, Probable's spec declares no operationId fields anywhere (grep -c operationId core/src/exchanges/probable/api.ts → 0), so method names are auto-derived by generateMethodName (core/src/utils/openapi.ts:15-24), which strips path segments that start with {:

function generateMethodName(httpMethod: string, path: string): string {
    const segments = path.split('/').filter(s => s && !s.startsWith('{'));
    const pascalPath = segments.map(toPascalCase).join('');
    return httpMethod.toLowerCase() + pascalPath;
}

Probable's paths use a trailing-slash convention for collections (/public/api/v1/events/) vs. a bare {id} suffix for single-item lookups (/public/api/v1/events/{id}); the trailing slash produces an empty path segment that the s && filter also drops, so both forms reduce to the identical segment list ['public','api','v1','events'] and generate the same method name getPublicApiV1Events. Same collision for markets:

  • GET /public/api/v1/events/ (core/src/exchanges/probable/api.ts:211) → getPublicApiV1Events
  • GET /public/api/v1/events/{id} (api.ts:273) → getPublicApiV1Events (collision, declared later, wins)
  • GET /public/api/v1/markets/ (api.ts:327) → getPublicApiV1Markets
  • GET /public/api/v1/markets/{id} (api.ts:358) → getPublicApiV1Markets (collision, declared later, wins)

Because parseOpenApiSpec (core/src/utils/openapi.ts:72-79) assigns endpoints[name] = {...} while iterating Object.entries(spec.paths) in declaration order, the later single-item path overwrites the earlier collection path in the generated endpoint map. The bare collection endpoints become unreachable under their natural generated name from either SDK's callApi().

Core

core/src/exchanges/probable/api.ts:211,273,327,358 (colliding path declarations) and core/src/utils/openapi.ts:15-24,72-79 (shared root-cause logic — the same collision mechanism flagged for Myriad in a separate issue). Note this is narrower in blast radius than the Myriad case: ProbableExchange's own unified fetchMarkets/fetchEvents paths (core/src/exchanges/probable/fetcher.ts fetchRawMarketsList/fetchRawEventsList/fetchRawEventById/fetchRawEventBySlug) bypass the implicit API entirely and call this.ctx.http.get(...) directly with a raw axios client, so Probable's own unified methods are unaffected. Only the raw callApi() escape hatch exposed to SDK consumers is affected.

TypeScript SDK

Missing — sdks/typescript/pmxt/client.ts's generic callApi(operationId, params) (line 755) cannot reach the bare GET /public/api/v1/events/ or GET /public/api/v1/markets/ endpoints under any name; callApi('getPublicApiV1Events')/callApi('getPublicApiV1Markets') always hits the single-item variant.

Python SDK

Missing — sdks/python/pmxt/client.py's call_api(operation_id, params) (line 1226) has the identical limitation.

Evidence

grep -n '"/public/api/v1/events\|"/public/api/v1/markets' core/src/exchanges/probable/api.ts confirms declaration order: /public/api/v1/events/ (211) before /public/api/v1/events/{id} (273); /public/api/v1/markets/ (327) before /public/api/v1/markets/{id} (358). grep -c operationId core/src/exchanges/probable/api.ts → 0, confirming no explicit disambiguation exists.

Impact

An SDK consumer using the callApi() raw-passthrough escape hatch to list all Probable events or markets by their natural generated method name will instead hit the single-item lookup endpoint (which expects an {id} path substitution), receiving an error or unexpected response instead of a collection — with no way to reach the true list endpoint via the implicit API in either SDK.


Found by automated Core-to-SDK surface coverage audit

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2: mediumbugSomething isn't workingcoreCore sidecar/server logiccore-sdk-gapCore engine capabilities not exposed in SDKseffort: medium

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions