aaif-goose / aaif-goose/goose

ACP: a failed/unreachable-provider turn is reported as a successful session/prompt (stopReason: end_turn, no JSON-RPC error)

Open
#11,047 1 comment 0 reactions 1 assignee Claimed by @jbg View on GitHub
Dominant language
Rust
Stars
54.2k
Forks
6.2k
Avg merge
3d 4h
Merged PRs (30d)
240

Description

**Describe the bug**

Over ACP, when the configured provider is completely unreachable, `session/prompt` does not return a JSON-RPC error and `stopReason` does not indicate failure. The network failure is instead surfaced only as ordinary assistant text, and the request completes as `end_turn`. An automated ACP client cannot detect this failure from the JSON-RPC contract at all — it would have to pattern-match human-readable, locale-specific text in the streamed content.

---

**To Reproduce**

1. Configure `GOOSE_PROVIDER=ollama` with no Ollama daemon running (or otherwise make the configured provider fully unreachable).
2. Connect over ACP (`goose acp` over stdio), `initialize`, create a session, send `session/prompt` with a simple prompt.
3. Observe the exchange:

```
→ {"id":4,"method":"session/prompt","params":{"sessionId":"…","prompt":[{"type":"text","text":"Say hi"}]}}
← notif session/update {"sessionUpdate":"session_info_update","_meta":{"goose":{"activeRunId":"run_…"}}}
(logs: 10× "Request failed, retrying (n/10): NetworkError(\"Could not connect to localhost:11434…\")"
"Backing off for 2.2s / 5.1s / 11.2s / 17.7s … before retry")
← notif session/update {"sessionUpdate":"agent_message_chunk","content":{"type":"text",
"text":"Network error: Could not connect to localhost:11434 — check your network connection and try again.\n\nPlease resend your m…"}}
← notif session/update {"sessionUpdate":"session_info_update","_meta":{"goose":{"activeRunId":null}}}
← notif session/update {"sessionUpdate":"usage_update","used":0,"size":128000}
← {"id":4,"result":{"stopReason":"end_turn"}}
```

No JSON-RPC `error` object anywhere in the exchange; `result.stopReason` is `"end_turn"`, identical to a successful turn.

---

**Expected behavior**

Not prescribing a specific fix, but as options: a distinct `stopReason` (e.g. an error/refusal variant) or a structured error field on the response would let a programmatic ACP client distinguish "the model answered" from "the provider was unreachable and goose gave up" without string-matching assistant text.

---

**Please provide the following information**

- **OS & Arch:** Linux x86_64
- **Interface:** ACP (stdio), `goose acp`
- **Version:** `1.45.0-canary+eea5609` (`goose-x86_64-unknown-linux-musl`, `canary` release tag)
- **Extensions enabled:** none
- **Provider & Model:** Ollama, unreachable (`GOOSE_PROVIDER=ollama`, no daemon running)

---

**Additional context**

- The whole failure took **~105 seconds across ~10 retries**. This latency is already tracked for the Desktop UI in #10917 ("Connection-refused to a down local provider is retried for ~2 minutes before surfacing an error") and mid-stream transient errors in #10897 — both about the same retry/backoff mechanism (`crates/goose-provider-types/src/retry.rs`), so linking those for context rather than duplicating the latency complaint. This issue is specifically about the **JSON-RPC success/failure contract** over ACP, not the latency.
- We set `GOOSE_PROVIDER_SKIP_BACKOFF=1` to shorten the repro and it had no visible effect. Looking at `retry.rs`, `skip_backoff` is parsed with `std::env::var(...).parse::().unwrap_or(false)` — Rust's `bool::from_str` only accepts the literals `"true"`/`"false"` (case-sensitive), so `"1"` fails to parse and silently falls back to `false`. That's consistent with what we observed, though we haven't confirmed whether `"true"` behaves differently — flagging it as a likely explanation, not a verified fix.
- Tested **without real provider credentials** — Ollama was simply not running, purely to get past the "no provider configured" wall using a free/local provider. This means we only exercised the network-failure path; we did not verify what a genuinely successful ACP turn's `stopReason`/`result` looks like, only that this particular failure path is indistinguishable from one.

Contributor guide

Open the contributing guide

Research direction

The issue is in the ACP (Agent Communication Protocol) handling of provider failures. Start by examining the JSON-RPC response flow in the session/prompt handler, likely in crates related to ACP or provider communication. Look at the retry logic in crates/goose-provider-types/src/retry.rs and how errors are propagated to the JSON-RPC layer. The goal is to modify the contract so a network failure results in a distinct stopReason or a structured error field, not just end_turn. Test by making a provider unreachable and verifying the ACP response.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
backend-api-design
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.