Sitelet https://github.com/earendil-works/pi/issues/8684
Skip to content

PI_OFFLINE silently disables all provider model discovery — undocumented behavior contradicting its documented scope #8684

Description

@mxr576

What happened?

PI_OFFLINE is documented as disabling only startup housekeeping network operations (update checks, package update checks, install/update telemetry). In practice, it also disables all provider model-catalog network discovery for the entire session — this is undocumented and easy to hit by accident.

Verified this is core behavior, not an extension issue, by reading the source directly (not just via pi -ne, since the affected code path doesn't touch any extension at all):

packages/coding-agent/src/core/model-runtime.ts, lines 190–196:

​ts const runtime = new ModelRuntime( credentials, config, modelsPath, modelsStore, providers, process.env.PI_OFFLINE === undefined, // becomes `modelNetworkEnabled` ); ​

That value is stored as modelNetworkEnabled and used as the default allowNetwork for every model refresh, line 701:

​ts const refreshOptions = { ...options, allowNetwork: options.allowNetwork ?? this.modelNetworkEnabled, }; ​

So merely having PI_OFFLINE set in the environment (to any value — the check is === undefined, not a truthiness/1 check, so even PI_OFFLINE=0 or PI_OFFLINE= triggers it) disables network-based model discovery everywhere that doesn't explicitly override allowNetwork: true. Providers relying on the default refresh path never discover models for the whole session, with no error and no indication this is what happened.

This contradicts the documented scope of PI_OFFLINE:

  • packages/coding-agent/docs/settings.md, line 84: "Use --offline or PI_OFFLINE=1 to disable all startup network operations described here, including update checks, package update checks, and install/update telemetry." (model discovery is not "described here")
  • packages/coding-agent/docs/environment-variables.md, line 84: "Disable startup network operations, including update checks, package updates, and install/update telemetry" (same omission)
  • packages/coding-agent/src/cli/args.ts, line 433 (--help text): "Disable startup network operations when set to 1/true/yes" (same omission)

It's also inconsistent with pi's own built-in llama extension, which deliberately keeps user-initiated model discovery alive under PI_OFFLINE:

https://github.com/earendil-works/pi/blob/v0.84.3/packages/coding-agent/src/extensions/llama/index.ts#L54-L58

That comment establishes the intended contract: PI_OFFLINE should suppress automatic/startup network access, while a user-initiated action against an explicitly-configured provider is a sanctioned exception. The modelNetworkEnabled default in model-runtime.ts applies the offline gate much more broadly than that — any provider/extension that doesn't specially override allowNetwork: true (as llama does) loses discovery entirely, including in response to explicit user actions like opening /model.

This matters in practice for anyone setting PI_OFFLINE=1 purely for its documented purpose — e.g. in an immutable/sandboxed environment (such as a DDEV add-on) to suppress update-check/telemetry phone-home — who then finds an explicitly configured, reachable model provider shows zero models with no explanation.

Steps to reproduce

. Configure any provider that relies on network model discovery through the default ModelRuntime.refresh() path (not one that special-cases allowNetwork: true like llama does) — e.g. the litellm provider via LITELLM_BASE_URL/LITELLM_API_KEY from the pi-provider-litellm extension, pointed at a real, reachable endpoint.
2. Set PI_OFFLINE=1 in the environment.
3. Run pi --list-models (or start pi interactively and open /model).

Result: no models are discovered, with no error message referencing PI_OFFLINE or offline mode anywhere.

  1. Unset PI_OFFLINE and repeat step 3 with identical configuration: models are discovered normally.
PI_OFFLINE=1 LITELLM_BASE_URL="https://your-litellm-proxy.example.com" LITELLM_API_KEY="sk-..." pi --list-models
// -> "No models available." — no mention of PI_OFFLINE/offline anywhere
env -u PI_OFFLINE LITELLM_BASE_URL="https://your-litellm-proxy.example.com" LITELLM_API_KEY="sk-..." pi --list-models
//  -> lists all discovered models
​```

Expected behavior

One of:

  • PI_OFFLINE's documentation (settings.md, environment-variables.md, --help text) explicitly states that it also disables automatic model-catalog network discovery, so this isn't a surprise; and/or
  • Model-network gating is decoupled from PI_OFFLINE into its own flag, since users set PI_OFFLINE for its documented, narrower purpose (no update/telemetry phone-home) and don't expect it to also silently break model discovery for an explicitly configured provider; and/or
  • The modelNetworkEnabled default is narrowed to match the llama extension's contract: suppress only automatic/startup discovery under PI_OFFLINE, while still allowing user-initiated discovery (e.g. opening /model) to proceed, consistent with the exception llama already carves out for itself.

At minimum, when discovery is skipped due to PI_OFFLINE, this should be surfaced to the user (or to extensions, via the refresh result) rather than failing indistinguishably from "not configured" or "provider unreachable".

Version

0.84.3

Activity

  1. github-actions commented on Aug 26, 2026

    @github-actions
    Contributor

    This issue was auto-closed. All issues from new contributors are auto-closed by default.

    Maintainers review auto-closed issues daily and reopen worthwhile ones. Issues that do not meet the quality bar in CONTRIBUTING.md will not be reopened or receive a reply.

    If a maintainer replies lgtmi on one of your issues, your future issues will stay open. If a maintainer replies lgtm, your future issues and PRs will stay open. The command must be at the start of the reply (optionally after one or more @username mentions) or at the end.

    See CONTRIBUTING.md.

  2. added
    untriagedThis issue has been auto closed and has not been triaged
    on Aug 26, 2026
  3. mxr576 commented on Aug 31, 2026

    @mxr576
    Author

    @mitsuhiko What is your thought on this? What is the real and expected meaning of PI_OFFLINE?

  4. removed
    untriagedThis issue has been auto closed and has not been triaged
    on Sep 1, 2026
  5. chiptoe-svg commented on Sep 3, 2026

    @chiptoe-svg

    Takeaway: confirmed on 0.84.4. Any value of PI_OFFLINE, including 0 and empty, stops catalog discovery in rpc mode, static providers are unaffected, and the fix is to parse the variable in model-runtime.ts the same way main.ts already does.

    I run pi in rpc mode in production with PI_OFFLINE=1, so I tested against a fake local server that logs every request, with one static models.json provider and one extension provider using createProvider with fetchModels (the same refreshModels path litellm uses). With PI_OFFLINE unset, an rpc session sends exactly one GET /v1/models and get_available_models lists the discovered models. With PI_OFFLINE=1, PI_OFFLINE=0, PI_OFFLINE= or --offline, the server never sees a request and only static models come back, with nothing in the output saying why.

    The === undefined check is the cause: patching that one test in the installed bundle to a 1/true/yes check made 0 and empty discover again while 1 stayed offline, and restoring it made them go dark again. Static models.json providers and the built-in catalog worked in every case, and a previously discovered model cached in models-store.json kept working offline. One difference from the report: pi --list-models never triggers discovery for me even with the variable unset, since that runtime is built without allowModelNetwork; the contrast only shows with a warm cache.

    So the flag really does disable discovery for any value. main.ts already parses it with isTruthyEnvFlag for offlineMode; using the same parse in model-runtime.ts fixes 0 and empty, and a line in docs/settings.md saying PI_OFFLINE also stops catalog discovery covers the rest.

  6. holny commented on Sep 4, 2026

    @holny

    I can take this. Root cause confirmed as described: model-runtime.ts gates modelNetworkEnabled with process.env.PI_OFFLINE === undefined (any value — even PI_OFFLINE=0 or empty — trips it), and refresh() defaults allowNetwork to it, so all network discovery silently dies for the session. This also contradicts the --offline/--help docs, which say 1/true/yes.

    Planned fix, scoped to the uncontroversial parts first:

    1. Replace the === undefined check with a truthy 1/true/yes check matching the documented CLI semantics.
    2. Surface the skip: when a refresh bails because of offline mode, include that in the refresh result / surface a warning instead of failing indistinguishably from "provider unreachable".
    3. Update the three doc sites (settings.md, environment-variables.md, --help) to state that automatic model-catalog network discovery is also disabled.

    One open question for you: the deeper fix is narrowing the default to match the llama extension's contract (offline suppresses startup/automatic discovery only, user-initiated refreshes like /model still hit the network). That requires marking which refresh() call sites are user-initiated (model-registry, model-catalog-refresh, etc. currently pass bare { signal }), so it touches several call paths. Happy to include that if you want it in the same change — otherwise I'd keep this PR to 1-3 and leave the narrowing as a follow-up.

    Requesting lgtm to open a PR.

    This comment is AI-generated by /wr.

  7. chiptoe-svg commented on Sep 5, 2026

    @chiptoe-svg

    I went ahead and put a patch together since I'd already reproduced this: main...chiptoe-svg:pi:fix/pi-offline-parse

    It's one shared isOfflineModeEnabled() with the 1/true/yes parse (main.ts, the package manager and the tools manager each already had their own copy of it), and every PI_OFFLINE read goes through it. That covers the === undefined check in model-runtime.ts plus four other spots that were testing for presence or plain truthiness - radius auth, the version check, and the three startup checks in interactive mode. Docs and --help now mention catalog discovery.

    I kept the narrowing question (user-initiated /model refreshes under offline) out of it - that's your call.

    Tests cover the parser and what allowNetwork a dynamic provider gets from the default refresh() under unset/0/empty/1; the 0 and empty cases fail without the fix. biome, tsgo and the runtime suites pass.

    @holny happy to hand this over if you'd rather run with it.

    Would appreciate an lgtm so I can open the PR.

  8. gaoanze888 commented on Sep 8, 2026

    @gaoanze888

    Confirmed on macOS. The root cause is sharper than "any value disables": PI_OFFLINE is parsed three different ways across the codebase, and model-runtime.ts uses the strictest one.

    • core/model-runtime.ts:196 passes process.env.PI_OFFLINE === undefined as modelNetworkEnabled. So PI_OFFLINE=0 or ="" (a user explicitly trying to turn offline off) still disables catalog discovery — the opposite of intent.
    • utils/version-check.ts:55 uses a truthy check, so PI_OFFLINE=0/=false also disables version checks.
    • main.ts:565 uses isTruthyEnvFlag(...), matching the docs in cli/args.ts:433 ("set to 1/true/yes").

    isTruthyEnvFlag (main.ts:106) returns true only for 1/true/yes. model-runtime.ts:196 is the outlier.

    Fix: replace process.env.PI_OFFLINE === undefined with !isTruthyEnvFlag(process.env.PI_OFFLINE) in model-runtime.ts:196, and align version-check.ts:55 the same way. This bites most in rpc mode, which skips main.ts:565's --offline → PI_OFFLINE="1" normalization, so a stray PI_OFFLINE=0 in the env isn't rewritten.

    Notable: model-runtime.ts has no direct test for the PI_OFFLINE → modelNetworkEnabled path (those tests live in package-manager.test.ts), which is why the regression went uncaught.


    Drafted with AI assistance; reviewed and shaped by me before posting.

  9. holny commented on Sep 9, 2026

    @holny

    Nudge — my fix for this is committed on a branch rebased against latest main (tests green). chiptoe-svg also has a compare-link patch upthread, so a lgtm for either approach would unblock this. (Drafted with AI assistance per CONTRIBUTING.md, reviewed and posted by me.)

  10. holny commented on Sep 14, 2026

    @holny

    Another nudge from me — fix is ready and tested, happy to open a PR whenever a maintainer has a minute. Also open to adjusting the approach if you'd prefer something else.

  11. mxr576 commented on Sep 17, 2026

    @mxr576
    Author

    I think you should just open the PR, can't you open it?

  12. balcsida commented on Sep 17, 2026

    @balcsida

    @mxr576 not until they say LGTM

  13. 1 remaining item

  14. mxr576 commented on Sep 21, 2026

    @mxr576
    Author

    @mitsuhiko Hi, I see major improvements are merged day by day to PI! Kudos! 🍻

    I wonder, is this small issue and the proposed fix(eS) on the team's radar? Could these guy get a LGTM so they can open a PR for review?

  15. added 2 commits that reference this issue on Sep 22, 2026
    c69e59d
    7ba778e
  16. added a commit that references this issue on Sep 22, 2026
    25cc5c7
  17. added a commit that references this issue on Sep 23, 2026
    d2a2d7b
  18. added a commit that references this issue on Sep 26, 2026
    dcc013b
  19. added a commit that references this issue on Oct 7, 2026
    dff54d1
  20. added a commit that references this issue on Oct 8, 2026
    d3f9bf8
  21. added a commit that references this issue on Oct 9, 2026
    ab6107a
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

bugSomething isn't working

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions