Problem
A standalone zeroshot task run can finish with status: failed and error: null even when the provider printed an actionable failure reason.
On current main:
consumeStderr() writes stderr to the log but does not retain a diagnostic.
complete() populates error only for the special fatal detector, session-identity failure, or signal termination.
detectProviderFatalError() is Claude-only and currently recognizes one fatal text pattern.
- A provider-agnostic
classifyProviderError() already exists, but the watcher terminal path does not use it.
This was observed with Gemini exiting 1 after printing IneligibleTierError / UNSUPPORTED_CLIENT: the useful cause remained buried in the log while task status exposed no error. Similar ordinary non-zero Claude exits can have the same result.
The provider rejecting a request is external. Zeroshot discarding the reason is the core bug.
Expected behavior
Every unsuccessful provider exit should persist a bounded, sanitized, actionable failure summary and surface it through task status / inspect.
Acceptance criteria
- A non-zero exit without a signal produces a non-empty task error when the provider emitted a diagnostic.
- Terminal handling uses the registered provider adapter's error classifier for every provider.
- The persisted result distinguishes at least retryable vs permanent/unsupported failures.
- Diagnostic capture is bounded and redacts likely credentials/secrets; complete raw output remains in the task log.
- Signal-only failures and existing session-identity failures retain their more specific reasons.
- Successful exits are unchanged.
- Fake-provider tests cover non-zero stderr/stdout failures for at least Claude, Codex, and Gemini, including the no-signal case.
zeroshot task status and zeroshot inspect show the persisted reason without requiring users to mine the log.
Related
#152 covers cluster-level provider retry/recovery. This issue is the narrower standalone task watcher/status boundary.
Problem
A standalone
zeroshot task runcan finish withstatus: failedanderror: nulleven when the provider printed an actionable failure reason.On current
main:consumeStderr()writes stderr to the log but does not retain a diagnostic.complete()populateserroronly for the special fatal detector, session-identity failure, or signal termination.detectProviderFatalError()is Claude-only and currently recognizes one fatal text pattern.classifyProviderError()already exists, but the watcher terminal path does not use it.This was observed with Gemini exiting 1 after printing
IneligibleTierError/UNSUPPORTED_CLIENT: the useful cause remained buried in the log while task status exposed no error. Similar ordinary non-zero Claude exits can have the same result.The provider rejecting a request is external. Zeroshot discarding the reason is the core bug.
Expected behavior
Every unsuccessful provider exit should persist a bounded, sanitized, actionable failure summary and surface it through
task status/inspect.Acceptance criteria
zeroshot task statusandzeroshot inspectshow the persisted reason without requiring users to mine the log.Related
#152 covers cluster-level provider retry/recovery. This issue is the narrower standalone task watcher/status boundary.