#1048 DSL retry_policy retries non-retryable errors and ignores Retry-After; orchestrator never passes retry_at to jumper
Description
EditEARS: When a tool step raises an error the orchestrator classifies as non-retryable (NON_RETRYABLE_ERROR_TYPES / patterns), the DSL retry_policy shall not retry it. When a tool error carries retry_after_seconds, the next attempt (DSL durable sleep or jumper retry) shall not start before that time, within the task's retry policy.
FOUND in #1044: (1) engine/interpreters/operators/task.py _execute_with_retry retries EVERY Exception up to retry_policy.max_retries with the fixed retry_policy.delay; it never consults is_retryable_error, so a 401 from an LLM provider is retried max_retries times. (2) engine/orchestrator/failure_handler.py fail_run -> jumper_client.fail_task(conn, run_id, reason) never passes retry_at, although jumper.fail_run(p_retry_at) supports it; the exception object is available in execution_helpers_outcome.handle_failure but only str(error) reaches fail_run. Effect for #1044: Prism 429/503 Retry-After is honoured only inside the adapter's bounded in-call retry (<= [llm.providers.prism] max_retry_after_seconds = 30 s); a longer reset (e.g. daily quota) surfaces as LLMRateLimitedError(retry_after_seconds) and the outer retry uses its own fixed/exponential delay. Proposal: a generic contract (exception attributes retryable: bool, retry_after_seconds: float) read by task.py and by handle_failure -> fail_run(retry_at=max(policy delay, now + retry_after)); tests both directions (blocks before reset, resumes after).
Comments
Loading comments...
Context
Loading context...
Audit History
View AllLoading audit history...