Skip to content

UpstreamClientContract's route regex times out on loaded runners and can eject any PR #426

Closed

Description

@Blind-Striker

UpstreamClientContract.ParseRoutes (tests/OpenCode.Sdk.Tools.Tests/Generator/Binding/Support/UpstreamClientContract.cs:129) matches the whole upstream client source with the RouteCall generated regex:

\(input\??: (?<input>\w+)Input, requestOptions\?: RequestOptions\)(?:: [^=]*?)? =>\s*\w+<[^\n]*?>\(\s*\{\s*method: "(?<method>\w+)",\s*path: `(?<path>[^`]*)`,(?:\s*query: \{(?<query>[^}]*)\},)?

The pattern has matchTimeoutMilliseconds: 1000. Its lazy wildcards (?:: [^=]*?)? and [^\n]*? backtrack across the source, so one matching pass can exceed one second on a loaded runner.

Observed: in the merge-group run for #424 on macOS (https://github.com/opencode-dotnet/opencode-sdk-dotnet/actions/runs/39012584027), three UpstreamClientContractTests failed with RegexMatchTimeoutException, including Parse_Should_Prefer_The_First_Source_That_Declares_An_Input. The same tests had passed on macOS in #424's own PR CI. #424 changes no Tools code.

Effect: the failure depends on runner load, not on the change under test, so it can eject any PR from the merge queue. #424 is held until this is fixed, rather than re-enqueued as a retry.

Fix direction: make the match linear. For example, split the client into per-route-call chunks first and match each chunk, or anchor the pattern so [^=]*? and [^\n]*? cannot scan across declarations. Otherwise, size the timeout to the input, with the reason stated. No retry or quarantine.

Activity

  1. added this to the 1.0 milestone on Oct 17, 2026
  2. Blind-Striker commented on Oct 17, 2026

    @Blind-Striker
    CollaboratorAuthor

    Correction to the diagnosis above: Generator measured the pattern, and it does not backtrack. RouteCall takes about 1 ms on the real 81 KB client and stays linear at 40× that size. The three failures landed within 0.4 s of each other, which fits a stalled runner exceeding a wall-clock timeout, not an expensive match. The effect stands: the test result depends on runner load. #428 removes the wall-clock timeout from the test-side regexes over fixed input, with the reason in each file, and adds a 40×-size scale row. #427 covers the tool-side sites.

  3. Blind-Striker commented on Oct 18, 2026

    @Blind-Striker
    CollaboratorAuthor

    Fixed by #428: the route regexes over trusted input use Timeout.Infinite, and the measurements in #428 rule out backtracking as the cause. Closing; found in the pre-1.0 issue cleanup.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions