From 38b8a7f4d0b9d22820419b241936602f61945719 Mon Sep 17 00:00:00 2001 From: Cahid Arda Date: Thu, 8 Oct 2026 22:12:49 +0000 Subject: [PATCH 1/4] DX-3022: docs: MCP Tasks failure behavior (isError, error details) Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01Gtucop3k8CNVLjgvoDTC8n --- redis/sdks/agentkit/mcp-tasks.mdx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/redis/sdks/agentkit/mcp-tasks.mdx b/redis/sdks/agentkit/mcp-tasks.mdx index 6d740bde..409422ab 100644 --- a/redis/sdks/agentkit/mcp-tasks.mdx +++ b/redis/sdks/agentkit/mcp-tasks.mdx @@ -320,6 +320,11 @@ tasks.define( route throws on its first request instead of running anything unverified. - When your handler throws, the task is not marked failed. Only the dispatcher marks it `failed`, and only after QStash has stopped retrying. The failed message stays in the QStash DLQ. + - When your handler *returns* a tool error (`isError: true`), the task is `failed` at once, with no + retry, and `task_status` returns your content with `isError`, as the synchronous tool would. + - The model only sees a failure's `error.code` and `error.message`. The transport's details + (`error.data`: the DLQ id, the failing response) stay in Redis for you, and a dispatch error is + logged rather than returned. - By default QStash tries 5 times with backoff `min(pow(3, retried) * 1000, 300000)`, about two minutes in total, so a task survives a server restart. The free tier and the local dev server allow at most 5 retries. From 1086052b26af11b30d0b8f4d0c9cdd7bed1b9a44 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 8 Oct 2026 22:13:27 +0000 Subject: [PATCH 2/4] chore(llms): regenerate llms.txt and llms-full.txt --- llms-full.txt | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/llms-full.txt b/llms-full.txt index ca78c6d7..734a4a6a 100644 --- a/llms-full.txt +++ b/llms-full.txt @@ -88499,6 +88499,11 @@ tasks.define( route throws on its first request instead of running anything unverified. * When your handler throws, the task is not marked failed. Only the dispatcher marks it `failed`, and only after QStash has stopped retrying. The failed message stays in the QStash DLQ. + * When your handler *returns* a tool error (`isError: true`), the task is `failed` at once, with no + retry, and `task_status` returns your content with `isError`, as the synchronous tool would. + * The model only sees a failure's `error.code` and `error.message`. The transport's details + (`error.data`: the DLQ id, the failing response) stay in Redis for you, and a dispatch error is + logged rather than returned. * By default QStash tries 5 times with backoff `min(pow(3, retried) * 1000, 300000)`, about two minutes in total, so a task survives a server restart. The free tier and the local dev server allow at most 5 retries. @@ -88913,6 +88918,11 @@ tasks.define( route throws on its first request instead of running anything unverified. * When your handler throws, the task is not marked failed. Only the dispatcher marks it `failed`, and only after QStash has stopped retrying. The failed message stays in the QStash DLQ. + * When your handler *returns* a tool error (`isError: true`), the task is `failed` at once, with no + retry, and `task_status` returns your content with `isError`, as the synchronous tool would. + * The model only sees a failure's `error.code` and `error.message`. The transport's details + (`error.data`: the DLQ id, the failing response) stay in Redis for you, and a dispatch error is + logged rather than returned. * By default QStash tries 5 times with backoff `min(pow(3, retried) * 1000, 300000)`, about two minutes in total, so a task survives a server restart. The free tier and the local dev server allow at most 5 retries. From fcc67e1a93ba7ddc431f7b84dc2489a2d5896bfa Mon Sep 17 00:00:00 2001 From: Cahid Arda Date: Thu, 8 Oct 2026 22:19:15 +0000 Subject: [PATCH 3/4] DX-3022: docs: what a handler threw is logged, not stored Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01Gtucop3k8CNVLjgvoDTC8n --- redis/sdks/agentkit/mcp-tasks.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/redis/sdks/agentkit/mcp-tasks.mdx b/redis/sdks/agentkit/mcp-tasks.mdx index 409422ab..74a86c4f 100644 --- a/redis/sdks/agentkit/mcp-tasks.mdx +++ b/redis/sdks/agentkit/mcp-tasks.mdx @@ -323,8 +323,8 @@ tasks.define( - When your handler *returns* a tool error (`isError: true`), the task is `failed` at once, with no retry, and `task_status` returns your content with `isError`, as the synchronous tool would. - The model only sees a failure's `error.code` and `error.message`. The transport's details - (`error.data`: the DLQ id, the failing response) stay in Redis for you, and a dispatch error is - logged rather than returned. + (`error.data`: the QStash DLQ id, the Workflow run id) stay in Redis for you. What your handler + threw and a dispatch error are logged, never stored or returned. - By default QStash tries 5 times with backoff `min(pow(3, retried) * 1000, 300000)`, about two minutes in total, so a task survives a server restart. The free tier and the local dev server allow at most 5 retries. From 348d01d0a589d31cea2660565310cbe5d2f8bb61 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 8 Oct 2026 22:19:40 +0000 Subject: [PATCH 4/4] chore(llms): regenerate llms.txt and llms-full.txt --- llms-full.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/llms-full.txt b/llms-full.txt index 734a4a6a..4d358b5d 100644 --- a/llms-full.txt +++ b/llms-full.txt @@ -88502,8 +88502,8 @@ tasks.define( * When your handler *returns* a tool error (`isError: true`), the task is `failed` at once, with no retry, and `task_status` returns your content with `isError`, as the synchronous tool would. * The model only sees a failure's `error.code` and `error.message`. The transport's details - (`error.data`: the DLQ id, the failing response) stay in Redis for you, and a dispatch error is - logged rather than returned. + (`error.data`: the QStash DLQ id, the Workflow run id) stay in Redis for you. What your handler + threw and a dispatch error are logged, never stored or returned. * By default QStash tries 5 times with backoff `min(pow(3, retried) * 1000, 300000)`, about two minutes in total, so a task survives a server restart. The free tier and the local dev server allow at most 5 retries. @@ -88921,8 +88921,8 @@ tasks.define( * When your handler *returns* a tool error (`isError: true`), the task is `failed` at once, with no retry, and `task_status` returns your content with `isError`, as the synchronous tool would. * The model only sees a failure's `error.code` and `error.message`. The transport's details - (`error.data`: the DLQ id, the failing response) stay in Redis for you, and a dispatch error is - logged rather than returned. + (`error.data`: the QStash DLQ id, the Workflow run id) stay in Redis for you. What your handler + threw and a dispatch error are logged, never stored or returned. * By default QStash tries 5 times with backoff `min(pow(3, retried) * 1000, 300000)`, about two minutes in total, so a task survives a server restart. The free tier and the local dev server allow at most 5 retries.