Skip to content

Build fails on missing crossterm; settings.env is ignored by the API client (ANTHROPIC_AUTH_TOKEN / ANTHROPIC_BASE_URL read via std::env instead of process_env) #4

Description

@skyflyfish

Environment

  • OS: Windows 10 22H2 (native, MSVC toolchain — not WSL)
  • Rust: 1.98.1 (edition 2024)
  • CometixCode: 0.2.0 (Claude Code)
  • Provider setup: cc-switch local proxy (ANTHROPIC_BASE_URL=http://127.0.0.1:15721,
    ANTHROPIC_AUTH_TOKEN=PROXY_MANAGED)

Issue 1 — Windows build failure: crossterm is not declared

src/utils/asciicast.rs:157 uses crossterm::terminal::size() in the #[cfg(not(unix))]
branch, but Cargo.toml never declares the dependency. The unix branch uses ioctl
instead, so the crate compiles fine on macOS/Linux — this only breaks on Windows.

error[E0433]: failed to resolve: use of undeclared crate or module `crossterm`
   --> src\utils\asciicast.rs:157

Fix — add to [dependencies] (version aligned with what iocraft already pulls in,
so it does not cause a second build of the crate):

crossterm = "0.29.0"

Issue 2 — settings.json env never reaches the API client

Root cause

src/utils/managed_env.rs writes every settings.env entry into the process_env
carrier — the module docs state "All writes go through the process_env carrier"
and never into std::env.

However src/services/api/client.rs reads the API-related configuration with
std::env::var(), so those values are always None.

Symptom A — Could not resolve authentication method

// src/services/api/client.rs — FirstParty branch
let resolved_api_key = if is_claude_ai_subscriber() {
    None
} else {
    api_key.or_else(crate::utils::auth::get_anthropic_api_key)  // only ANTHROPIC_API_KEY
};

let auth_token = if is_claude_ai_subscriber() {
    crate::utils::auth::get_claude_ai_oauth_tokens().map(|t| t.access_token)
} else {
    None                                                       // always None
};

configure_api_key_headers() (:84) does put ANTHROPIC_AUTH_TOKEN into
default_headers as Authorization: Bearer …, but auth_token handed to
anthropic_sdk::Anthropic::new() stays None, so the SDK rejects the client:

Could not resolve authentication method. Expected either apiKey or authToken to be set.

Any setup that only sets ANTHROPIC_AUTH_TOKEN (cc-switch, and most third-party relays)
hits this unconditionally.

Symptom B — API requests silently go to api.anthropic.com

// src/services/api/client.rs:459
let base_url = if /* ant staging */ {
    Some(...)
} else {
    env::var("ANTHROPIC_BASE_URL").ok()   // std::env -> always None under settings.env
};

With base_url == None the SDK falls back to https://api.anthropic.com, which replies:

[403] 403 {"error":{"type":"forbidden","message":"Request not allowed"}}

This is very misleading to diagnose: switching providers has no effect at all
(the request never reaches the relay), while export ANTHROPIC_BASE_URL=... in the shell
makes it work (that path does land in std::env).

Suggested fix

Read through process_env first, falling back to std::env so both sources work
(settings.jsonprocess_env; shell exportstd::env):

// client.rs — configure_api_key_headers()
let token = crate::utils::process_env::var("ANTHROPIC_AUTH_TOKEN")
    .or_else(|| env::var("ANTHROPIC_AUTH_TOKEN").ok())
    .filter(|t| !t.trim().is_empty());

// client.rs — FirstParty branch
let auth_token = if is_claude_ai_subscriber() {
    crate::utils::auth::get_claude_ai_oauth_tokens().map(|t| t.access_token)
} else {
    crate::utils::process_env::var("ANTHROPIC_AUTH_TOKEN")
        .or_else(|| std::env::var("ANTHROPIC_AUTH_TOKEN").ok())
        .filter(|t| !t.trim().is_empty())
};

// client.rs — base_url
let base_url = if /* ant staging */ {
    Some(...)
} else {
    crate::utils::process_env::var("ANTHROPIC_BASE_URL")
        .or_else(|| env::var("ANTHROPIC_BASE_URL").ok())
};

And for consistency in src/utils/model/providers.rs:

pub fn is_first_party_anthropic_base_url() -> bool {
    let base_url = crate::utils::process_env::var("ANTHROPIC_BASE_URL")
        .or_else(|| std::env::var("ANTHROPIC_BASE_URL").ok());
    is_first_party_anthropic_base_url_for_audience(
        base_url.as_deref(),
        crate::utils::build_profile::build_audience(),
    )
}

A broader alternative would be to have managed_env mirror its writes into std::env,
which would fix every std::env::var() reader at once — but the four call sites above
are the ones that matter for API requests.

Steps to reproduce

  1. Put a relay config in ~/.claude/settings.json:
    { "env": { "ANTHROPIC_AUTH_TOKEN": "PROXY_MANAGED",
               "ANTHROPIC_BASE_URL": "http://127.0.0.1:15721" } }
  2. Run cometix -p "hi"Could not resolve authentication method
  3. Apply the auth_token fix only → [403] Request not allowed
    (the request went to api.anthropic.com, not to 127.0.0.1:15721)
  4. Apply the base_url fix → works

Verification after the fix

With no environment variables set at all, relying only on settings.json:

$ cometix -p "hi"
Hi! How can I help you today?

Note

There may be more std::env::var() readers of settings.env-settable keys
(ANTHROPIC_MODEL, ANTHROPIC_BETAS, ANTHROPIC_CUSTOM_HEADERS, …).
A sweep over env::var("ANTHROPIC_*") in src/ would be worth doing.

Activity

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

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions