@cfxlabsinc/b2b-services
    Preparing search index...

    Type Alias RouteResolutionFailure

    Which gate produced a NO_ELIGIBLE_ROUTE, and everything the resolver knew about it.

    Rides on the error as a ServiceError detail rather than as a new error code: NO_ELIGIBLE_ROUTE is public API surface (TServiceError in virtual-account-api) with ~30 consumers, so splitting the code would be a breaking contract change plus an exhaustiveness cascade — to fix what is an internal debugging problem. ServiceError<Code, Details> exists for exactly this: one code, several causes.

    • ACTIVATION — a route matched the criteria but its activation rule did not approve it. activationReason and routeId are both set.
    • CANDIDACY — no route survived its route matcher and its vendor's mandatory_matcher, so there is no row to describe and both are absent.
    type RouteResolutionFailure = {
        activationReason?: ResolvedRoute["reason"];
        criteria?: PinnedMatcherCriteria;
        routeId?: string;
        stage: "ACTIVATION" | "CANDIDACY";
    }
    Index
    activationReason?: ResolvedRoute["reason"]
    criteria?: PinnedMatcherCriteria

    The criteria the resolver evaluated, in the same JSON-safe pinned form product_quote.data stores. Lets an operator surface replay this exact call in the quote simulator instead of hand-assembling seven fields and hoping they match — a near-miss reproduction answers a DIFFERENT question than the call that failed, which is the failure mode this whole change exists to kill.

    Stays inside the process. TServiceError declares no details schema for NO_ELIGIBLE_ROUTE, so Fastify's serializer emits only code and message and this never crosses the public REST boundary; the admin dashboard reads it in-process through a server action. Anything added here must stay safe under that assumption — check the response schema before widening it.

    routeId?: string
    stage: "ACTIVATION" | "CANDIDACY"