{
  "schemaVersion": "1.0",
  "title": "Dify streaming, cancellation, and binary responses",
  "description": "This reference defines the Dify connector response paths after an action passes admission. Use it to select an execution mode and preserve uncertainty when a provider task cannot be proved stopped.",
  "canonical": "https://getaip.org/docs/connectors/dify/reference/streaming-cancellation-and-binary-responses",
  "route": "/docs/connectors/dify/reference/streaming-cancellation-and-binary-responses",
  "source": "docs/connectors/dify/reference/streaming-cancellation-and-binary-responses.md",
  "protocol": "Agent Interoperability Protocol",
  "protocolVersion": "1.0",
  "section": "Connectors",
  "documentType": "Connector",
  "language": "en",
  "downloads": {
    "md": "/docs/download/connectors/dify/reference/streaming-cancellation-and-binary-responses.md",
    "txt": "/docs/download/connectors/dify/reference/streaming-cancellation-and-binary-responses.txt",
    "json": "/docs/download/connectors/dify/reference/streaming-cancellation-and-binary-responses.json",
    "pdf": "/docs/download/connectors/dify/reference/streaming-cancellation-and-binary-responses.pdf"
  },
  "content": {
    "format": "text/markdown",
    "markdown": "---\ntitle: Dify streaming, cancellation, and binary responses\ndescription: >-\n  Consume bounded Dify event streams, preserve cancellation evidence, and\n  decode binary response envelopes\nkind: reference\naudience: application-developer\nappliesTo: \"1.x\"\nwritingStandard: \"aip-docs/1.0\"\nlastReviewedRevision: \"97be86e9efedf07ecf1783b03800f683f107fb04\"\nconnector: dify\n---\n\n# Dify streaming, cancellation, and binary responses\n\nThis reference defines the Dify connector response paths after an action passes\nadmission. Use it to select an execution mode and preserve uncertainty when a\nprovider task cannot be proved stopped.\n\nThe connector has three distinct response treatments: normalized SSE, bounded\nbinary data, and buffered JSON or text. The catalogue, not an upstream\n`Content-Type` guess, selects the treatment.\n\n## Select the response path\n\n| Capability | Typed invocation | Synchronous invocation | Cancellation contract |\n|---|---|---|---|\n| App alias | Normalized SSE | Forced blocking JSON | Remote stop after task identity is known |\n| `workflow.run_by_id` | Normalized SSE | Forced blocking JSON | Remote workflow stop |\n| `workflow.event.stream` | Normalized SSE | Buffered response | Close the local subscription only |\n| Other app operation | Buffered response | Buffered response | Not supported |\n| Knowledge operation | Buffered response | Buffered response | Not supported |\n\nTyped invocation forces `response_mode: streaming` for an app alias and\n`workflow.run_by_id`. Synchronous invocation forces `response_mode: blocking`\nfor the same execution routes.\n\n`workflow.event.stream` is already an SSE route. Cancelling its AIP action must\nnot infer a request to stop the underlying workflow.\n\nProvider pipeline run and node-run endpoints may use SSE internally. Their\nfrozen catalogue entries declare JSON responses, so this connector buffers\nthem and does not advertise AIP streaming or cancellation.\n\n## Consume normalized SSE\n\nThe parser accepts LF or CRLF frame delimiters. It collects all `data` lines in\na frame, joins them with newline characters, and parses the result as JSON.\n\nComment-only and field-only frames produce no AIP chunk. A literal `[DONE]`\nbecomes a synthetic `message_end` event.\n\nEvery parsed event must contain an `event` string. The connector uses `data`\nas the event payload when present; otherwise, it retains the full provider\nobject.\n\n### Map event kinds\n\n| Dify event | AIP stream kind | Special result behavior |\n|---|---|---|\n| `workflow_started`, `node_started` | Progress | None |\n| `tool_call` | Tool | None |\n| `agent_thought` | Thought | None |\n| `message` | Data | Appends a non-empty `answer` delta |\n| `human_input_required` | Pending approval | Records human-input data |\n| `workflow_paused` | Pending approval | Terminal and records human-input data |\n| `error` | Error | Produces a failed action result |\n| `message_end`, `workflow_finished` | Done | Terminal |\n| Any other event | Progress | Provider data remains available |\n\nAn unsuccessful `workflow_finished` status produces a failed result even\nthough the event is terminal. HTTP success therefore does not imply successful\nworkflow completion.\n\nThe emitted sequence begins at zero and increases once per parsed event.\nMessage deltas are also accumulated for the final result.\n\n### Recognize terminal outcomes\n\nThe terminal events are `message_end`, `workflow_finished`, and\n`workflow_paused`. An upstream close before any terminal event is an invalid\nstream.\n\nWhen message deltas exist, the completed output contains the assembled answer\nand final event. Without deltas, the final event data becomes the output.\n\nRecorded human-input data changes the final action outcome to requires-human.\nIt remains attached with the final event rather than being flattened into a\nnormal completion.\n\n### Enforce stream bounds\n\n| Bound | Frozen value or source |\n|---|---|\n| Complete response or stream | Configured maximum, 8 MiB by default |\n| Maximum configurable response | 64 MiB |\n| Undelimited SSE frame | 1 MiB |\n| Emitted events | 10,000 |\n| Accumulated answer | Configured response maximum |\n\nA transport chunk may contain several complete frames and may exceed 1 MiB.\nOnly the undelimited remainder is subject to the frame bound.\n\nThe complete byte count includes every upstream transport chunk. A declared\n`Content-Length` above the configured maximum is rejected before body\ncollection when that header is available.\n\n## Preserve durable cancellation evidence\n\nThe connector correlates an AIP action ID with three provider values:\n\n```text\napp_id + task_id + user\n```\n\nThe first parsed event carrying `task_id`, either at the top level or under\n`data`, creates or replaces that durable mapping. The same `user` value used to\nstart the action is stored because Dify stop routes require it.\n\nA cluster-safe deployment uses the profile-state task store. The in-memory\nstore is suitable only when restart and replica handoff do not need to preserve\ncancellation evidence.\n\n### Interpret cancellation certainty\n\n| State at cancellation | Connector action | Reported certainty |\n|---|---|---|\n| Request not yet admitted or no task ID observed | Interrupt locally | Remote stop unconfirmed |\n| Durable task mapping exists | Call the mode-specific stop route | Confirmed after success or 404 |\n| Explicit `task_id` and valid `user` are supplied | Reconstruct task identity and call stop | Confirmed after success or 404 |\n| Event subscription capability | Close local response stream | No workflow-stop claim |\n| Non-cancellable explicit operation | Reject cancellation | No provider mutation |\n\nThe stop route is selected from the configured app mode. Workflow,\ncompletion, and chat-family apps use different provider paths.\n\nA provider 404 is accepted as an idempotent stop outcome. It proves that the\ntarget task is no longer available at the stop route, not why it disappeared.\n\nAfter confirmed stop, normal terminal completion, or provider-declared failure,\nthe connector removes the durable mapping.\n\n## Handle stream failure cleanup\n\nMalformed UTF-8, invalid JSON, a missing event name, exceeded bounds, and\npremature close fail stream processing. The connector then attempts to stop a\nknown active task.\n\nIf cleanup itself cannot be confirmed, the returned stream error includes that\nuncertainty. Never translate this state into a successful cancellation or a\nsafe retry decision.\n\nMutating app aliases and workflow runs remain unsafe to retry. An idempotency\nkey controls replay identity but does not prove that an interrupted provider\nexecution had no effects.\n\n## Decode binary envelopes\n\nExactly three frozen operations declare binary responses:\n\n- `file.preview`\n- `audio.synthesize`\n- `document.download_zip`\n\nThe connector reads the body under the configured response bound and returns\nthis envelope:\n\n```json\n{\n  \"http_status\": 200,\n  \"content_base64\": \"<base64 bytes>\",\n  \"content_type\": \"application/octet-stream\",\n  \"size_bytes\": 128,\n  \"provider_request_id\": \"<optional x-request-id>\"\n}\n```\n\n`content_type` and `provider_request_id` can be absent. Consumers must decode\n`content_base64`, verify `size_bytes`, and apply a trusted media policy before\nopening or persisting the result.\n\nThe connector does not infer a filename or parse the binary payload. Base64\nexpansion also means the AIP output is larger than the provider body.\n\n## Interpret buffered non-binary responses\n\nAll other explicit operations return a bounded envelope containing\n`http_status`, `body`, `content_type`, and optional `provider_request_id`.\n\nValid JSON becomes the `body` value. Empty content becomes null. Non-JSON bytes\nbecome a lossy UTF-8 `text` field, so this fallback is diagnostic rather than a\nbinary-safe transport.\n\nProvider error responses are decoded under the same response bound. Preserve\nthe mapped AIP failure and retry metadata instead of branching only on the\nembedded provider body.\n\n## Diagnose response failures\n\n| Observation | First decision |\n|---|---|\n| No chunks arrive | Verify typed invocation and an SSE-declared capability |\n| Stream closes without completion | Treat the outcome as invalid and inspect cleanup certainty |\n| Cancellation is unconfirmed | Look for durable task identity; do not replay the mutation |\n| Cancellation stops event delivery but workflow continues | Confirm the capability is `workflow.event.stream` |\n| Pipeline run has no AIP chunks | Its frozen response kind is JSON |\n| Binary body appears as text | Verify the operation is one of the three binary catalogue entries |\n| Response exceeds a bound | Reduce provider output or raise the configured limit within 64 MiB |\n| Workflow ends with HTTP 200 and failed status | Use the action result, not HTTP status alone |\n\n## Related documentation\n\n- [Dify connector overview](../README.md)\n- [Run Dify chat and workflows](../guides/run-chat-and-workflows.md)\n- [Dify workflows, streaming, and human input](../capabilities/workflows-streaming-and-human-input.md)\n- [Dify connector configuration](configuration.md)\n- [Errors and retry decisions](../../../reference/errors.md)\n",
    "text": "Dify streaming, cancellation, and binary responses\n\nThis reference defines the Dify connector response paths after an action passes\nadmission. Use it to select an execution mode and preserve uncertainty when a\nprovider task cannot be proved stopped.\n\nThe connector has three distinct response treatments: normalized SSE, bounded\nbinary data, and buffered JSON or text. The catalogue, not an upstream\nContent-Type guess, selects the treatment.\n\nSelect the response path\n\n| Capability | Typed invocation | Synchronous invocation | Cancellation contract |\n\n| App alias | Normalized SSE | Forced blocking JSON | Remote stop after task identity is known |\n| workflow.runbyid | Normalized SSE | Forced blocking JSON | Remote workflow stop |\n| workflow.event.stream | Normalized SSE | Buffered response | Close the local subscription only |\n| Other app operation | Buffered response | Buffered response | Not supported |\n| Knowledge operation | Buffered response | Buffered response | Not supported |\n\nTyped invocation forces responsemode: streaming for an app alias and\nworkflow.runbyid. Synchronous invocation forces responsemode: blocking\nfor the same execution routes.\n\nworkflow.event.stream is already an SSE route. Cancelling its AIP action must\nnot infer a request to stop the underlying workflow.\n\nProvider pipeline run and node-run endpoints may use SSE internally. Their\nfrozen catalogue entries declare JSON responses, so this connector buffers\nthem and does not advertise AIP streaming or cancellation.\n\nConsume normalized SSE\n\nThe parser accepts LF or CRLF frame delimiters. It collects all data lines in\na frame, joins them with newline characters, and parses the result as JSON.\n\nComment-only and field-only frames produce no AIP chunk. A literal [DONE]\nbecomes a synthetic messageend event.\n\nEvery parsed event must contain an event string. The connector uses data\nas the event payload when present; otherwise, it retains the full provider\nobject.\n\nMap event kinds\n\n| Dify event | AIP stream kind | Special result behavior |\n\n| workflowstarted, nodestarted | Progress | None |\n| toolcall | Tool | None |\n| agentthought | Thought | None |\n| message | Data | Appends a non-empty answer delta |\n| humaninputrequired | Pending approval | Records human-input data |\n| workflowpaused | Pending approval | Terminal and records human-input data |\n| error | Error | Produces a failed action result |\n| messageend, workflowfinished | Done | Terminal |\n| Any other event | Progress | Provider data remains available |\n\nAn unsuccessful workflowfinished status produces a failed result even\nthough the event is terminal. HTTP success therefore does not imply successful\nworkflow completion.\n\nThe emitted sequence begins at zero and increases once per parsed event.\nMessage deltas are also accumulated for the final result.\n\nRecognize terminal outcomes\n\nThe terminal events are messageend, workflowfinished, and\nworkflowpaused. An upstream close before any terminal event is an invalid\nstream.\n\nWhen message deltas exist, the completed output contains the assembled answer\nand final event. Without deltas, the final event data becomes the output.\n\nRecorded human-input data changes the final action outcome to requires-human.\nIt remains attached with the final event rather than being flattened into a\nnormal completion.\n\nEnforce stream bounds\n\n| Bound | Frozen value or source |\n\n| Complete response or stream | Configured maximum, 8 MiB by default |\n| Maximum configurable response | 64 MiB |\n| Undelimited SSE frame | 1 MiB |\n| Emitted events | 10,000 |\n| Accumulated answer | Configured response maximum |\n\nA transport chunk may contain several complete frames and may exceed 1 MiB.\nOnly the undelimited remainder is subject to the frame bound.\n\nThe complete byte count includes every upstream transport chunk. A declared\nContent-Length above the configured maximum is rejected before body\ncollection when that header is available.\n\nPreserve durable cancellation evidence\n\nThe connector correlates an AIP action ID with three provider values:\n\nappid + taskid + user\n\nThe first parsed event carrying taskid, either at the top level or under\ndata, creates or replaces that durable mapping. The same user value used to\nstart the action is stored because Dify stop routes require it.\n\nA cluster-safe deployment uses the profile-state task store. The in-memory\nstore is suitable only when restart and replica handoff do not need to preserve\ncancellation evidence.\n\nInterpret cancellation certainty\n\n| State at cancellation | Connector action | Reported certainty |\n\n| Request not yet admitted or no task ID observed | Interrupt locally | Remote stop unconfirmed |\n| Durable task mapping exists | Call the mode-specific stop route | Confirmed after success or 404 |\n| Explicit taskid and valid user are supplied | Reconstruct task identity and call stop | Confirmed after success or 404 |\n| Event subscription capability | Close local response stream | No workflow-stop claim |\n| Non-cancellable explicit operation | Reject cancellation | No provider mutation |\n\nThe stop route is selected from the configured app mode. Workflow,\ncompletion, and chat-family apps use different provider paths.\n\nA provider 404 is accepted as an idempotent stop outcome. It proves that the\ntarget task is no longer available at the stop route, not why it disappeared.\n\nAfter confirmed stop, normal terminal completion, or provider-declared failure,\nthe connector removes the durable mapping.\n\nHandle stream failure cleanup\n\nMalformed UTF-8, invalid JSON, a missing event name, exceeded bounds, and\npremature close fail stream processing. The connector then attempts to stop a\nknown active task.\n\nIf cleanup itself cannot be confirmed, the returned stream error includes that\nuncertainty. Never translate this state into a successful cancellation or a\nsafe retry decision.\n\nMutating app aliases and workflow runs remain unsafe to retry. An idempotency\nkey controls replay identity but does not prove that an interrupted provider\nexecution had no effects.\n\nDecode binary envelopes\n\nExactly three frozen operations declare binary responses:\n• file.preview\n• audio.synthesize\n• document.downloadzip\n\nThe connector reads the body under the configured response bound and returns\nthis envelope:\n\n{\n  \"httpstatus\": 200,\n  \"contentbase64\": \"\",\n  \"contenttype\": \"application/octet-stream\",\n  \"sizebytes\": 128,\n  \"providerrequestid\": \"\"\n}\n\ncontenttype and providerrequestid can be absent. Consumers must decode\ncontentbase64, verify sizebytes, and apply a trusted media policy before\nopening or persisting the result.\n\nThe connector does not infer a filename or parse the binary payload. Base64\nexpansion also means the AIP output is larger than the provider body.\n\nInterpret buffered non-binary responses\n\nAll other explicit operations return a bounded envelope containing\nhttpstatus, body, contenttype, and optional providerrequestid.\n\nValid JSON becomes the body value. Empty content becomes null. Non-JSON bytes\nbecome a lossy UTF-8 text field, so this fallback is diagnostic rather than a\nbinary-safe transport.\n\nProvider error responses are decoded under the same response bound. Preserve\nthe mapped AIP failure and retry metadata instead of branching only on the\nembedded provider body.\n\nDiagnose response failures\n\n| Observation | First decision |\n\n| No chunks arrive | Verify typed invocation and an SSE-declared capability |\n| Stream closes without completion | Treat the outcome as invalid and inspect cleanup certainty |\n| Cancellation is unconfirmed | Look for durable task identity; do not replay the mutation |\n| Cancellation stops event delivery but workflow continues | Confirm the capability is workflow.event.stream |\n| Pipeline run has no AIP chunks | Its frozen response kind is JSON |\n| Binary body appears as text | Verify the operation is one of the three binary catalogue entries |\n| Response exceeds a bound | Reduce provider output or raise the configured limit within 64 MiB |\n| Workflow ends with HTTP 200 and failed status | Use the action result, not HTTP status alone |\n\nRelated documentation\n• Dify connector overview (../README.md)\n• Run Dify chat and workflows (../guides/run-chat-and-workflows.md)\n• Dify workflows, streaming, and human input (../capabilities/workflows-streaming-and-human-input.md)\n• Dify connector configuration (configuration.md)\n• Errors and retry decisions (../../../reference/errors.md)\n"
  },
  "integrity": {
    "algorithm": "sha256",
    "sourceDigest": "da1849799fffb194c8d9cb507e02dc0dfb3249980ba1e875be4a261aa0b80507"
  }
}
