Kuberwastaken/claurst · critical

AnthropicProvider::from_config: failed to create…

Error message

AnthropicProvider::from_config: failed to create AnthropicClient

What it means

AnthropicProvider::from_config builds an AnthropicClient from the given ClientConfig and calls .expect() on the result, panicking if client construction fails. The panic message surfaces this string. Construction fails when the config cannot produce a valid HTTP client (e.g. TLS backend init failure).

Solutions

  1. Prefer the fallible constructor (AnthropicClient::new / provider constructors that return Result) and propagate the error instead of relying on from_config.
  2. Check that exactly one TLS backend is available (feature flags: rustls vs native-tls) in your build.
  3. If cross-compiling, ensure the TLS library links correctly for the target.
  4. Enable the `tls` feature of reqwest if all TLS features were disabled.
  5. If this panic fires in practice, capture the underlying reqwest build error by constructing AnthropicClient::new(config) manually and inspecting the Result.

Example fix

// before
let provider = AnthropicProvider::from_config(config); // panics on failure
// after
let client = AnthropicClient::new(config).context("creating Anthropic client")?;
let provider = AnthropicProvider { client: Arc::new(client), id: ProviderId::new(ProviderId::ANTHROPIC) };
Defensive patterns

Strategy: validation

Validate before calling

// ensure reqwest has a TLS backend at build time
cargo tree -i reqwest  # verify default-tls or rustls-tls is enabled
// and construct the client early with a proper error instead of from_config
let client = AnthropicClient::new(config).map_err(|e| anyhow!("Anthropic client init failed: {e}"))?;

Try / catch

// avoid the panicking path; use the fallible construction and catch it
match AnthropicClient::new(config) {
    Ok(client) => Ok(AnthropicProvider { client: Arc::new(client), id: ProviderId::new(ProviderId::ANTHROPIC) }),
    Err(e) => Err(anyhow!("failed to create Anthropic client: {e}")),
}

Prevention

When it happens

Trigger: Calling AnthropicProvider::from_config with a ClientConfig whose reqwest client builder fails to build — practically only TLS backend initialization failure, since config shape is fixed.

Common situations: Static-linking or cross-compiling without a TLS backend (rustls/native-tls mismatch); running in an environment where OpenSSL cannot be initialized. Note the crate convention elsewhere is to propagate; this expect is an internal invariant.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


AI-assisted analysis of Kuberwastaken/claurst@b0637c97ec (2026-09-10). Data as JSON: /api/errors/4931acfbe9f2896b. Report an issue: GitHub.

Appendix: source

Thrown at src-rust/crates/api/src/providers/anthropic.rs:52

/// `Arc<dyn LlmProvider>`.
pub struct AnthropicProvider {
    client: Arc<AnthropicClient>,
    id: ProviderId,
}

impl AnthropicProvider {
    /// Wrap an already-constructed (and Arc-wrapped) [`AnthropicClient`].
    pub fn new(client: Arc<AnthropicClient>) -> Self {
        Self {
            client,
            id: ProviderId::new(ProviderId::ANTHROPIC),
        }
    }

    /// Construct directly from a [`ClientConfig`], creating the inner client.
    pub fn from_config(config: ClientConfig) -> Self {
        let client = AnthropicClient::new(config)
            .expect("AnthropicProvider::from_config: failed to create AnthropicClient");
        Self {
            client: Arc::new(client),
            id: ProviderId::new(ProviderId::ANTHROPIC),
        }
    }

    /// Build a [`CreateMessageRequest`] from a [`ProviderRequest`].
    fn build_request(request: &ProviderRequest) -> CreateMessageRequest {
        let normalized_messages = normalize_anthropic_messages(&request.messages);
        let api_messages: Vec<ApiMessage> = normalized_messages
            .iter()
            .map(ApiMessage::from)
            .collect();

        let api_tools: Option<Vec<ApiToolDefinition>> = if request.tools.is_empty() {
            None
        } else {
            Some(request.tools.iter().map(ApiToolDefinition::from).collect())

View on GitHub (pinned to b0637c97ec)