Recreate a deleted temp directory instead of failing all later buffered requests
- Dominant language
- C#
- Stars
- 38.4k
- Forks
- 10.9k
- Avg merge
- 2d 10h
- Merged PRs (30d)
- 281
Description
### Is there an existing issue for this?
- [x] I have searched the existing issues
### Describe the bug
When ASP.NET Core spills a large multipart request or a large buffered response to disk, it writes `ASPNETCORE_*.tmp` under `ASPNETCORE_TEMP` or `Path.GetTempPath()`.
`AspNetCoreTempDirectory` caches that path for the process lifetime. If the directory is later missing (disk cleanup, an operator deleting the folder, Windows removing `Temp\{sessionId}`), the framework throws `DirectoryNotFoundException` instead of creating the directory it already resolved. Later requests that exceed the memory threshold keep failing until something outside the process recreates the folder.
For `IFormFile` binding this happens in `FormFeature` **before** user endpoint code runs, so the app cannot recover inside the action. Setting `ASPNETCORE_TEMP` to a “stable” folder does not help if that folder is deleted while the process is still running.
Related but not a duplicate of https://github.com/dotnet/aspnetcore/issues/42647 (locked). That issue was answered as: `%TEMP%` does not update after process start, so point `ASPNETCORE_TEMP` at a directory that already exists. This report is the other failure mode: **the path is already known, but the directory was deleted.**
### Expected Behavior
Before creating `ASPNETCORE_*.tmp`, call `Directory.CreateDirectory` on the already resolved path (no-op if it exists). If create or the subsequent open fails (permissions, read-only volume), throw as today.
Do not change how the path is chosen (`ASPNETCORE_TEMP` then `Path.GetTempPath()`). Do not try to refresh `%TEMP%` or allocate a new Windows session-id folder.
### Steps To Reproduce
1. Host an API that accepts `IFormFile` (multipart upload larger than `FormOptions.MemoryBufferThreshold` / the rewind threshold).
2. Optionally set `ASPNETCORE_TEMP` to a dedicated folder (the workaround from #42647), or use the default `Path.GetTempPath()`.
3. Ensure the folder exists, start the app, and confirm the upload succeeds.
4. Delete that folder. Do not restart the process.
5. Repeat the same upload.
### Exceptions (if any)
```text
2026-08-18 17:26:12.676 [DBG] Reading the request body failed with an IOException. |7|System.IO.DirectoryNotFoundException: C:\Users\Administrator\AppData\Local\Temp\2\
at Obfuscation.Web!+0x391f5
at Obfuscation.Web!+0x1090d1
at Obfuscation.Web!+0x10eeb0
--- End of stack trace from previous location ---
at Obfuscation.Web!+0x3767b0
at Obfuscation.Web!+0x378d07
at Obfuscation.Web!+0x378c2b
at Obfuscation.Web!+0x111b70
--- End of stack trace from previous location ---
at Obfuscation.Web!+0x3767b0
at Obfuscation.Web!+0x378d07
at Obfuscation.Web!+0x378c2b
at Obfuscation.Web!+0x4377e
--- End of stack trace from previous location ---
at Obfuscation.Web!+0x3767b0
at Obfuscation.Web!+0x378d07
at Obfuscation.Web!+0x378c2b
at Obfuscation.Web!+0x1d1e12
```
Often wrapped as:
```
Reading the request body failed with an IOException.
```
Typical stacks:
- Request: `FormFeature.InnerReadFormAsync` → `FileBufferingReadStream.CreateTempFile` → `AspNetCoreTempDirectory.TempDirectory` or `new FileStream(...)`
- Response: `FileBufferingWriteStream.EnsureFileStream` (same as #42647)
### .NET Version
10.0.400
### Anything else?
- ASP.NET Core version: current `main` (also observed on shipped versions that use `AspNetCoreTempDirectory`).
- OS: Windows (also applies anywhere the resolved temp directory can be deleted while the process is running).
Call path for `IFormFile`:
1. `FormFeature.InnerReadFormAsync` (`src/Http/Http/src/Features/FormFeature.cs`)
2. `section.EnableRewind(...)` → `FileBufferingReadStream` (`src/Http/Http/src/Internal/BufferingHelper.cs`)
3. Over threshold → `FileBufferingReadStream.CreateTempFile()` (`src/Http/WebUtilities/src/FileBufferingReadStream.cs`)
4. `AspNetCoreTempDirectory` currently throws if the directory is missing, and caches the path after the first successful lookup
I have a small patch ready (create the resolved directory in `AspNetCoreTempDirectory` and again immediately before creating the temp file in `FileBufferingReadStream` / `FileBufferingWriteStream`) and can open a PR against `main` if this approach is acceptable.
Contributor guide
Research direction
Start with FormFeature.InnerReadFormAsync and follow BufferingHelper to FileBufferingReadStream.CreateTempFile; inspect AspNetCoreTempDirectory and the corresponding FileBufferingWriteStream path. Reproduce by deleting ASPNETCORE_TEMP after the first buffered request, then retry a request and response over the threshold. Done means the resolved directory is recreated and buffering succeeds, while permission or read-only failures still surface.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- csharp
- Domain
- api, backend
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 76/100