Skip to content

[Bug]: Pi export rewrites OpenRouter router slug openrouter/free into an invalid model ID #8168

Closed as not planned

Description

@huenchunb

Summary

The Pi model export appears to normalize the OpenRouter router slug openrouter/free into openrouter-free. The exported model is then listed by Pi but cannot be invoked through the OpenCodex proxy: the upstream rejects it with HTTP 400. The canonical slug works when sent unchanged, and a combo targeting it is a viable workaround.

Environment

  • OpenCodex: 2.94.0
  • Client: Pi (ocx export --client pi)
  • OpenRouter is configured as a provider in OpenCodex; Pi talks to the local OpenCodex proxy.

Steps to reproduce

  1. Configure OpenRouter in OpenCodex and enable the OpenRouter Free Models Router (openrouter/free).

  2. Confirm the live proxy model catalog exposes the router as openrouter/openrouter/free from GET /v1/models (provider prefix plus the full upstream model slug).

  3. Export the catalog for Pi using ocx export --client pi --json.

  4. Observe that the exported Pi model entry is:

    { "id": "openrouter/openrouter-free", "name": "openrouter/free (openrouter)" }
  5. Invoke the exported entry through Pi, or send a Chat Completions request to the proxy with model: "openrouter/openrouter-free".

Actual behavior

The request fails with HTTP 400:

{
  "error": {
    "message": "Provider error 400: openrouter/openrouter-free is not a valid model ID",
    "type": "invalid_request_error",
    "code": "invalid_request_error"
  }
}

The ID advertised to Pi does not identify a valid model selector at the proxy/upstream boundary.

Expected behavior

The Pi export should contain an ID which the OpenCodex proxy accepts and routes to the configured OpenRouter router. In this case the live selector openrouter/openrouter/free works when sent through the proxy; OpenRouter responds with a concrete free model (for example, liquid/lfm-2.5-2.6b:free). The returned model can vary per request, as expected for openrouter/free.

Impact

Users selecting openrouter/free in OpenCodex and exporting their catalog to Pi see the model in Pi's picker, but calls fail. This makes the advertised router unusable via the normal exported-Pi workflow. Other provider/model slugs containing nested slashes may be affected if export or model resolution treats slash-separated IDs as only one provider/model separator.

Workaround confirmed

A combo with a stable combo ID and the canonical target works:

combo/subagents-free -> openrouter/openrouter/free

The combo exports as combo/subagents-free, Pi can call it successfully, and OpenCodex logs show it resolving to openrouter/free. This is functional, but users should not need to create a one-target combo merely to work around an incorrectly exported model ID.

Suggested fix

Preserve the canonical upstream model slug when generating the Pi catalog and when resolving requests. The provider/model boundary should be parsed unambiguously (provider is openrouter; the complete upstream model ID is openrouter/free), rather than flattening or replacing the slash in the model slug. Keep the exported ID, display name, and request resolver mapping consistent.

A regression test could:

  1. Add a provider model whose upstream ID is openrouter/free.
  2. Export the Pi catalog and assert that its model ID resolves to the same upstream selector (or to an explicit stable alias mapped to it).
  3. Send a request using the exported ID through the proxy and assert it reaches OpenRouter successfully and returns the concrete model from the router response.
  4. Include nested-slash model IDs in the test matrix so the provider prefix is separated from the full provider model ID without losing path segments.

I verified that openrouter/openrouter-free fails, while the live selector openrouter/openrouter/free succeeds and returns a concrete :free model. I also verified the combo workaround above. No API keys or credentials are included here.

Activity

  1. changed the title [-][Bug]: Pi export rewrites OpenRouter router slug into an invalid model ID[/-] [+][Bug]: Pi export rewrites OpenRouter router slug `openrouter/free` into an invalid model ID[/+] on Nov 4, 2026
  2. github-actions commented on Nov 4, 2026

    @github-actions
    Contributor

    Automated translation bookkeeping — detected language: English.

  3. coderabbitai commented on Nov 4, 2026

    @coderabbitai
    Contributor
    🔗 Related PRs

    #7971 - fix(router): decode routed slugs via modelDisplayNames ids [open]
    #7979 - fix(router): decode routed slugs via modelDisplayNames ids [merged]


    📝 Issue Planner

    Check the box below or use the @coderabbitai plan command to generate an implementation plan and prompts that you can use with your favorite coding assistant.

    • Create Plan

    🧪 Issue enrichment is currently in open beta.

    To disable automatic issue enrichment, add the following to your .coderabbit.yaml:

    issue_enrichment:
      auto_enrich:
        enabled: false

    💬 Have feedback or questions? Drop into our discord!

  4. github-actions commented on Nov 4, 2026

    @github-actions
    Contributor

    Issue closed: insufficient detail

    Thanks for taking the time to submit this issue. It was closed automatically because the structured report is missing information needed to evaluate or reproduce it.

    • Reproduction is empty.

    Please edit the issue to add:

    • List the exact steps to reproduce the problem.

    See the Contributing guide for details.

    Once the report passes the automated checks, it will be reopened automatically unless a maintainer has changed its state.

  5. huenchunb commented on Nov 4, 2026

    @huenchunb
    Author

    I have updated the report with exact reproduction steps and observed behavior. The fix should be scoped to Pi client export: preserve the canonical nested OpenRouter model ID so the generated catalog selector maps unambiguously to provider=openrouter and upstream model=openrouter/free. A one-target combo remains a verified workaround. I will investigate the export boundary and regression coverage in a fork.

  6. huenchunb commented on Nov 4, 2026

    @huenchunb
    Author

    I have updated the report with exact reproduction steps and observed behavior. The fix should be scoped to Pi client export: preserve the canonical nested OpenRouter model ID so the generated catalog selector maps unambiguously to provider=openrouter and upstream model=openrouter/free. A one-target combo remains a verified workaround. I will investigate the export boundary and regression coverage in a fork.

  7. huenchunb commented on Nov 4, 2026

    @huenchunb
    Author

    A fully structured bug report was opened as #8172 and passes the repository issue-quality validator. The proposed fix is in draft PR #8171 targeting dev; the old issue remains closed because GitHub rejected a state reopen (422). Please track the active report at #8172.

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

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions