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):
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.json → process_env; shell export → std::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
- Put a relay config in
~/.claude/settings.json:
{ "env": { "ANTHROPIC_AUTH_TOKEN": "PROXY_MANAGED",
"ANTHROPIC_BASE_URL": "http://127.0.0.1:15721" } }
- Run
cometix -p "hi" → Could not resolve authentication method
- Apply the
auth_token fix only → [403] Request not allowed
(the request went to api.anthropic.com, not to 127.0.0.1:15721)
- 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.
Environment
0.2.0 (Claude Code)ANTHROPIC_BASE_URL=http://127.0.0.1:15721,ANTHROPIC_AUTH_TOKEN=PROXY_MANAGED)Issue 1 — Windows build failure:
crosstermis not declaredsrc/utils/asciicast.rs:157usescrossterm::terminal::size()in the#[cfg(not(unix))]branch, but
Cargo.tomlnever declares the dependency. Theunixbranch usesioctlinstead, so the crate compiles fine on macOS/Linux — this only breaks on Windows.
Fix — add to
[dependencies](version aligned with whatiocraftalready pulls in,so it does not cause a second build of the crate):
Issue 2 —
settings.jsonenvnever reaches the API clientRoot cause
src/utils/managed_env.rswrites everysettings.enventry into theprocess_envcarrier — the module docs state "All writes go through the
process_envcarrier" —and never into
std::env.However
src/services/api/client.rsreads the API-related configuration withstd::env::var(), so those values are alwaysNone.Symptom A —
Could not resolve authentication methodconfigure_api_key_headers()(:84) does putANTHROPIC_AUTH_TOKENintodefault_headersasAuthorization: Bearer …, butauth_tokenhanded toanthropic_sdk::Anthropic::new()staysNone, so the SDK rejects the client: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.comWith
base_url == Nonethe SDK falls back tohttps://api.anthropic.com, which replies: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 shellmakes it work (that path does land in
std::env).Suggested fix
Read through
process_envfirst, falling back tostd::envso both sources work(
settings.json→process_env; shellexport→std::env):And for consistency in
src/utils/model/providers.rs:A broader alternative would be to have
managed_envmirror its writes intostd::env,which would fix every
std::env::var()reader at once — but the four call sites aboveare the ones that matter for API requests.
Steps to reproduce
~/.claude/settings.json:{ "env": { "ANTHROPIC_AUTH_TOKEN": "PROXY_MANAGED", "ANTHROPIC_BASE_URL": "http://127.0.0.1:15721" } }cometix -p "hi"→Could not resolve authentication methodauth_tokenfix only →[403] Request not allowed(the request went to
api.anthropic.com, not to127.0.0.1:15721)base_urlfix → worksVerification after the fix
With no environment variables set at all, relying only on
settings.json:Note
There may be more
std::env::var()readers ofsettings.env-settable keys(
ANTHROPIC_MODEL,ANTHROPIC_BETAS,ANTHROPIC_CUSTOM_HEADERS, …).A sweep over
env::var("ANTHROPIC_*")insrc/would be worth doing.