{"record":{"id":"8ca74099298d4014","repo":"microsoft/aspire","slug":"file-input-inputname-accepts-at-most-maxfilecount-filelabel","errorCode":null,"errorMessage":"File input '{inputName}' accepts at most {maxFileCount} {fileLabel}.","messagePattern":"File input '(.+?)' accepts at most (.+?) (.+?)\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/Aspire.Hosting/Dashboard/InteractionFileUploadStore.cs","lineNumber":69,"sourceCode":"        {\n            if (interaction.State != FileInteractionState.InProgress)\n            {\n                throw new InvalidOperationException($\"Interaction '{interactionId}' is not accepting file uploads.\");\n            }\n\n            if (!interaction.FileInputLimits.TryGetValue(inputName, out var maxFileCount))\n            {\n                throw new InvalidOperationException($\"Interaction '{interactionId}' is not accepting file uploads for input '{inputName}'.\");\n            }\n\n            // Each client submits one file selection per input during an interaction. Multi-file selections upload\n            // their files sequentially as part of that single selection, so every upload counts toward this limit.\n            // Count uploads in progress as reserved slots so concurrent requests cannot exceed the input's limit.\n            var fileCount = interaction.Files.Values.Count(entry => string.Equals(entry.InputName, inputName, StringComparisons.InteractionInputName));\n            if (fileCount >= maxFileCount)\n            {\n                var fileLabel = maxFileCount == 1 ? \"file\" : \"files\";\n                throw new InvalidOperationException($\"File input '{inputName}' accepts at most {maxFileCount} {fileLabel}.\");\n            }\n\n            // Keep only the leaf name as metadata. The client-supplied name is never used for the\n            // on-disk path because filename rules vary by platform and some names have special semantics.\n            var lastSep = originalFileName.AsSpan().LastIndexOfAny('/', '\\\\');\n            var safeName = lastSep >= 0 ? originalFileName[(lastSep + 1)..] : originalFileName;\n\n            var tempFile = _tempFileSystem.CreateTempFile();\n            var fileId = Guid.NewGuid().ToString(\"N\");\n\n            interaction.Files[fileId] = new FileEntry(tempFile, inputName, safeName);\n            _logger.LogDebug(\n                \"Created uploaded file entry {FileId} for interaction {InteractionId}, input {InputName}, and file {FileName}.\",\n                fileId,\n                interactionId,\n                inputName,\n                safeName);\n            return (fileId, tempFile.Path);","sourceCodeStart":51,"sourceCodeEnd":87,"githubUrl":"https://github.com/microsoft/aspire/blob/25830f84bd145686607ad00c057b3f84e2e51d43/src/Aspire.Hosting/Dashboard/InteractionFileUploadStore.cs#L51-L87","documentation":"Each file input declares a maximum file count (maxFileCount). The store counts existing and in-progress upload entries for that input and, since uploads count toward the limit even while in flight, any upload beyond the cap is rejected. The message uses singular/plural 'file/files' based on the limit.","triggerScenarios":"Calling CreateEntry for an input whose current fileCount already equals maxFileCount — e.g. uploading a third file to an input configured for two, or concurrent uploads racing to exceed the limit.","commonSituations":"Client-side UI allowing more files than the input's declared maximum; user selecting a multi-file set larger than the cap; double-submission of the same selection adding duplicate entries.","solutions":["Limit client-side file selection to the input's maxFileCount before submitting","Remove completed/unwanted entries for the input before uploading new files if the flow allows re-selection","Catch InvalidOperationException and inform the user of the maximum ('at most N files')","Serialize uploads per input and check the count immediately before each CreateEntry call"],"exampleFix":"// before\nforeach (var file in selectedFiles) // may exceed limit mid-loop\n    await store.CreateEntry(interactionId, inputName, file.Name, file.Stream);\n// after\nforeach (var file in selectedFiles.Take(maxFileCount))\n    await store.CreateEntry(interactionId, inputName, file.Name, file.Stream);","handlingStrategy":"validation","validationCode":"var current = interaction.Files.Values.Count(f => string.Equals(f.InputName, inputName, StringComparisons.InteractionInputName));\nif (current >= interaction.FileInputLimits[inputName])\n{\n    throw new InvalidOperationException($\"Input '{inputName}' already at its file limit.\");\n}","typeGuard":"bool UnderLimit(InteractionFileUploadStore.Interaction i, string inputName) =>\n    i.FileInputLimits.TryGetValue(inputName, out var max) &&\n    i.Files.Values.Count(f => f.InputName == inputName) < max;","tryCatchPattern":"try\n{\n    await store.CreateEntry(interactionId, inputName, fileName, stream);\n}\ncatch (InvalidOperationException ex) when (ex.Message.Contains(\"accepts at most\"))\n{\n    // Enforce the limit in the UI and inform the user.\n}","preventionTips":["Clamp client-side selections to maxFileCount before upload","Serialize uploads per input to avoid racing the count","Remove superseded entries before re-uploading a selection","Show the per-input limit in the upload UI"],"tags":["dashboard","file-upload","limit-exceeded"],"backgroundTag":"file-size-limit-exceeded","analyzedSha":"25830f84bd145686607ad00c057b3f84e2e51d43","analyzedAt":"2026-09-16T11:10:06.193Z","contentChangedAt":"2026-09-16T11:10:06.193Z","schemaVersion":2},"datasetVersion":"2026-09-21T09:17:21.228Z"}