zeroclaw-labs/zeroclaw · error · anyhow::Error
ACP request_permission timed out after {timeout:?}
Error message
ACP request_permission timed out after {timeout:?} What it means
The companion check in AzureOpenAiBuilder::build(): after resource_name, it reads deployment_name with this expect. The deployment name is the second half of the required URL pair — without it the https://{resource}.openai.azure.com/openai/deployments/{deployment} endpoint cannot be formed, which is why the builder documents the panic. It fires when deployment_name() was never called on the builder.
Source
Thrown at crates/zeroclaw-channels/src/acp_channel.rs:97
"options": options,
// `toolCall` is required by the ACP schema. We use a synthetic
// ask_user tool call so the client surfaces the prompt with a
// sensible title.
"toolCall": {
"toolCallId": format!("ask-user-{}", uuid::Uuid::new_v4()),
"title": question,
"kind": "other",
"status": "pending",
}
});
let call = self.rpc.request("session/request_permission", params);
let response = match tokio::time::timeout(timeout, call).await {
Ok(Ok(value)) => value,
Ok(Err(e)) => {
anyhow::bail!("ACP request_permission failed: {} ({})", e.message, e.code)
}
Err(_) => anyhow::bail!("ACP request_permission timed out after {timeout:?}"),
};
// Response shape: { outcome: { outcome: "selected", optionId: "..." } | { outcome: "cancelled" } }
let outcome = response.get("outcome");
let kind = outcome
.and_then(|o| o.get("outcome"))
.and_then(|s| s.as_str())
.unwrap_or("");
match kind {
"selected" => {
let option_id = outcome
.and_then(|o| o.get("optionId"))
.and_then(|s| s.as_str())
.unwrap_or("");
let idx = option_id
.strip_prefix("choice-")
.and_then(|s| s.parse::<usize>().ok());
match idx.and_then(|i| choices.get(i)) {View on GitHub (pinned to 88bb9c8533)
Solutions
- Set deployment_name() to the Azure deployment id (not the model name) before build().
- Validate both fields together when parsing provider config, failing with one actionable message listing both keys.
- Prefer a constructor taking resource and deployment as parameters so the type system enforces presence.
- Keep the listed builder tests green as a regression net.
Example fix
// before
let provider = AzureOpenAiModelProvider::builder()
.resource_name("my-resource")
.build(); // panics: deployment_name() is required
// after
let provider = AzureOpenAiModelProvider::builder()
.resource_name("my-resource")
.deployment_name("gpt-4o-deploy")
.build(); Defensive patterns
Strategy: validation
Validate before calling
// Check both halves of the URL pair before build():
fn azure_url_inputs_present(cfg: &AzureProviderConfig) -> Result<(), String> {
match (&cfg.resource_name, &cfg.deployment_name) {
(Some(r), Some(d)) if !r.is_empty() && !d.is_empty() => Ok(()),
_ => Err("azure provider needs both resource_name and deployment_name".into()),
}
} Type guard
fn azure_deployment_set(cfg: &serde_json::Value) -> bool {
cfg.get("deployment_name")
.and_then(|v| v.as_str())
.map_or(false, |s| !s.trim().is_empty())
} Try / catch
let p = std::panic::catch_unwind(|| {
AzureOpenAiModelProvider::builder()
.resource_name(r.clone())
.deployment_name(d.clone())
.build()
});
if p.is_err() { /* surface a config error naming resource_name and deployment_name */ } Prevention
- Use the Azure deployment id, not the model name, for deployment_name
- Validate resource+deployment together so one message covers both panics
- Alert on portal-side deployment renames that invalidate stored config
- Prefer required-parameter constructors in your own builders
When it happens
Trigger: build() invoked after setting resource_name but not deployment_name — e.g. config supplied the Azure resource yet omitted the deployment/model deployment id.
Common situations: Azure provider config that names the resource but not the deployment; deployments renamed in Azure portal so old config keys no longer map; test or tooling code constructing the builder with only one of the two required setters.
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- ACP request_permission failed: {} ({})
- ACP returned unknown optionId: {option_id}
- purge_agent not supported by this memory backend
- rename_agent not supported by this memory backend
- memory backend '{}' does not support StoreOptions kind/pinne
AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23).
Data as JSON: /api/errors/2a598fd39fd7ab5d.
Report an issue: GitHub.