BoundaryML/baml · error
Invalid client property. Should have been a anthropic…
Error message
Invalid client property. Should have been a anthropic property but got: {} What it means
resolve_properties expects the resolved client property block to be an Anthropic-specific property type; when the resolved variant is anything else, BAML bails with this type error naming what it got instead. It is an internal configuration-shape mismatch between the provider and the parsed client property.
Solutions
- Check that provider is exactly "anthropic" and the options block matches Anthropic's schema (api_key, model, etc.)
- Remove foreign options fields copied from other providers
- Ensure dynamic property injection returns an Anthropic-shaped property object
- Pin/align the BAML runtime/CLI versions so the anthropic property parser matches
Example fix
// before (baml)
client "claude" {
provider anthropic
options { api_key env.OPENAI_API_KEY base_url "https://api.openai.com" } // openai-shaped options
}
// after
client "claude" {
provider anthropic
options { api_key env.ANTHROPIC_API_KEY model "claude-3-5-sonnet-latest" }
} Defensive patterns
Strategy: validation
Validate before calling
// baml-src check or runtime config validation
function validateAnthropicClient(cfg) {
if (cfg.provider !== 'anthropic') throw new Error('provider must be anthropic');
for (const k of Object.keys(cfg.options)) {
if (!['api_key','model','base_url','max_tokens','temperature'].includes(k))
throw new Error(`unknown anthropic option: ${k}`);
}
} Type guard
const isAnthropicProps = (p) => p && typeof p === 'object' && p.type === 'anthropic_properties';
Try / catch
try {
return await b.functions.F(x);
} catch (e) {
if (/Invalid client property/.test(String(e))) {
throw new Error('Fix clients.baml: anthropic provider needs anthropic-shaped options');
}
throw e;
} Prevention
- Never copy options blocks between providers without adapting them
- Validate client configs with `baml-cli dev` test generation before shipping
- Keep provider string and options schema in sync
- Pin BAML versions across services
When it happens
Trigger: Declaring a client with provider "anthropic" whose options/properties resolve to a non-Anthropic ResolvedClientProperty variant — e.g. misconfigured properties block, wrong provider string, or custom properties that resolve to a generic variant.
Common situations: Copy-pasted client config from another provider (openai/aws) with provider renamed to anthropic but options shape unchanged; plugin/dynamic property resolution returning the wrong variant; version mismatch where anthropic options schema changed.
Related errors
- ai.Prompt._data must contain baml_builtins2::PromptAst
- array access on non-list at
- array access should be either map or array.
- array assignment on non-list value at
- assert condition must be boolean
AI-assisted analysis of BoundaryML/baml@bd85ce9dee (2026-09-12).
Data as JSON: /api/errors/5d038d912b7531e0.
Report an issue: GitHub.
Appendix: source
Thrown at engine/baml-runtime/src/internal/llm_client/primitive/anthropic/anthropic_client.rs:60
context: RenderContext_Client,
features: ModelFeatures,
properties: ResolvedAnthropic,
// clients
client: reqwest::Client,
}
// resolves/constructs PostRequestProperties from the client's options and runtime context, fleshing out the needed headers and parameters
// basically just reads the client's options and matches them to needed properties or defaults them
fn resolve_properties(
provider: &ClientProvider,
properties: &UnresolvedClientProperty<()>,
ctx: &RuntimeContext,
) -> Result<ResolvedAnthropic, anyhow::Error> {
let properties = properties.resolve(provider, &ctx.eval_ctx(false))?;
let ResolvedClientProperty::Anthropic(props) = properties else {
anyhow::bail!(
"Invalid client property. Should have been a anthropic property but got: {}",
properties.name()
);
};
Ok(props)
}
// getters for client info
impl WithRetryPolicy for AnthropicClient {
fn retry_policy_name(&self) -> Option<&str> {
self.retry_policy.as_deref()
}
}
impl WithClientProperties for AnthropicClient {
fn allowed_metadata(&self) -> &AllowedRoleMetadata {
&self.properties.allowed_metadata
}View on GitHub (pinned to bd85ce9dee)