TL;DR
- Check that
AsyncModeEnabled=trueand the request sends theS7PAsyncModeheader.- Verify the user profile has async configuration with a valid blob container and Service Bus topic.
- If blobs exist but are empty, the critical cause is
OutputStreamnot being cleared — see below.
Client sends request with S7PAsyncMode header
│
▼
Proxy processes normally → if processing exceeds AsyncTriggerTimeout:
│
▼
Proxy returns 202 immediately
│ (blob URI + SB topic in response body)
▼
AsyncWorker writes response → Blob Storage
│
▼
Status update written → Service Bus topic
All three must be true for async to activate:
| Condition | Setting | Env Var |
|---|---|---|
| System switch on | AsyncModeEnabled=true |
AsyncModeEnabled |
| Request header present | Header name = AsyncClientRequestHeader (default S7PAsyncMode) |
set on request |
| User profile has async config | Profile field async-config contains blob container + SB topic |
— |
AsyncTriggerTimeout (default 10 s) is the time a request must be in flight before async kicks in. If the backend responds in under 10 s, the request is returned synchronously — this is normal.
To force a request to go async sooner, reduce AsyncTriggerTimeout:
| Setting | Env Var | App Config key |
|---|---|---|
| Trigger timeout (ms) | AsyncTriggerTimeout=<ms> |
Warm:Async:TriggerTimeout |
AsyncBlobStorageConfig is a composite string:
AsyncBlobStorageConfig=uri=https://mystorageaccount.blob.core.windows.net,mi=trueFor managed identity (mi=true), the proxy identity needs Storage Blob Data Contributor on the storage account.
az role assignment create \
--assignee <principal-id> \
--role "Storage Blob Data Contributor" \
--scope "/subscriptions/<sub>/resourceGroups/<rg>/providers/Microsoft.Storage/storageAccounts/<account>"AsyncSBConfig is a composite string:
AsyncSBConfig=cs=<connection-string>,ns=<namespace>,q=requeststatus,mi=trueFor managed identity, the proxy identity needs Azure Service Bus Data Sender on the namespace or topic.
Warning
Critical bug (fixed Feb 2026): After the proxy sends the 202 response and closes the client connection, OutputStream must be explicitly set to null. If it is not, GetOrCreateDataStreamAsync() returns the already-closed client stream instead of opening a blob stream, and the backend response is written to nothing.
If you are running a custom build or patched version, verify AsyncWorker.cs contains:
_requestData.Context.Response.Close();
_requestData.OutputStream = null; // ← CRITICAL — must follow Close()This line must appear after Response.Close(). Without it, the blob will be created but will remain empty.
AsyncTriggerTimeoutmay be larger than the backend response time. The backend responded before async was triggered.- Verify the client is sending the
S7PAsyncModeheader.
- Check Service Bus connectivity — the status message (with the blob URI) may not be reaching the client.
- Check blob container name in the user profile
async-configfield. - Check
AsyncBlobWorkerCount— if set to 0 or the workers are all busy, blobs queue up.
AsyncTimeout (default 30 min) is the maximum async request lifetime. After this, the request is abandoned. If the backend takes longer than 30 min, increase AsyncTimeout.
| Setting | Env Var | App Config key |
|---|---|---|
| Max async lifetime (ms) | AsyncTimeout=<ms> |
Warm:Async:Timeout |
| Async request TTL after upgrade | AsyncTTLSecs=<s> |
Warm:Async:TTLSecs |
- AsyncOperation.md — full async configuration reference
- USER_PROFILES.md — how to configure async per user profile
- TIMEOUTS.md — TTL and timeout interactions