Skip to content

Improve error message for export operations in batch/transaction bundles#5475

Open
tranhoangtu-it wants to merge 2 commits intomicrosoft:mainfrom
tranhoangtu-it:fix/export-batch-error-message
Open

Improve error message for export operations in batch/transaction bundles#5475
tranhoangtu-it wants to merge 2 commits intomicrosoft:mainfrom
tranhoangtu-it:fix/export-batch-error-message

Conversation

@tranhoangtu-it
Copy link
Copy Markdown

Summary

Fixes #1459.

When users include a $export operation in a batch or transaction bundle, the server previously returned a cryptic error: "Value supplied for the 'Accept' header is not supported." This occurs because the internal bundle routing cannot set the required Accept and Prefer headers that export operations depend on.

Changes

  • BundleHandler.cs: Added early detection of $export URLs in bundle entries. Returns a clear RequestNotValidException with an informative error message before the request enters the routing pipeline.
  • Resources.resx / Resources.Designer.cs: Added new resource string ExportOperationNotSupportedInBundle.

New error message

Export operations ($export) are not supported within batch or transaction bundles. Please submit export requests as standalone operations.

This returns HTTP 400 with OperationOutcome.issue.code = "invalid", which aligns with the FHIR specification for invalid request parameters.

Test plan

  • Submit a bundle with $export entry → receives clear error message
  • Submit a bundle without $export → processes normally (no regression)
  • Standalone $export requests → continue to work as before

Third contribution as a certified HL7 FHIR Implementer. Also building FHIRBridge — an open-source FHIR integration toolkit.

…t#1459)

When a $export operation is included in a batch or transaction bundle,
the server previously returned a cryptic "Accept header is not
supported" error because the internal routing couldn't set the required
export-specific headers.

Now detects $export URLs early in bundle entry processing and returns
a clear error: "Export operations ($export) are not supported within
batch or transaction bundles."
@tranhoangtu-it tranhoangtu-it requested a review from a team as a code owner March 30, 2026 20:10
Copilot AI review requested due to automatic review settings March 30, 2026 20:10
@tranhoangtu-it
Copy link
Copy Markdown
Author

Required metadata labels:

  • Enhancement (type — improved error message)
  • Open source only (release target)
  • No-PaaS-breaking-change
  • No-ADR

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Improves the server’s response when a $export operation is included inside a batch/transaction Bundle, replacing a downstream “unsupported Accept header” failure with an explicit, user-friendly validation error before routing.

Changes:

  • Add an early guard in bundle request generation to detect $export URLs and fail fast with a clearer exception.
  • Introduce a new localized resource string for the error message.
  • Regenerate the resources designer accessor for the new string.

Reviewed changes

Copilot reviewed 2 out of 3 changed files in this pull request and generated 3 comments.

File Description
src/Microsoft.Health.Fhir.Shared.Api/Features/Resources/Bundle/BundleHandler.cs Adds early detection of $export in bundle entry request URLs and throws a validation exception with a clearer message.
src/Microsoft.Health.Fhir.Core/Resources.resx Adds ExportOperationNotSupportedInBundle string used by the new validation path.
src/Microsoft.Health.Fhir.Core/Resources.Designer.cs Adds the strongly-typed accessor for the new resource string.
Files not reviewed (1)
  • src/Microsoft.Health.Fhir.Core/Resources.Designer.cs: Language not supported

Comment on lines +545 to +547
if (requestUrl != null && requestUrl.Contains("$export", StringComparison.OrdinalIgnoreCase))
{
throw new RequestNotValidException(Core.Resources.ExportOperationNotSupportedInBundle);
Copy link

Copilot AI Mar 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The $export detection uses requestUrl.Contains("$export", ...), which will also match query-string values (e.g., Patient?name=$export) and can falsely reject non-export requests. Consider parsing requestUrl as a Uri and checking only the path segments for a segment equal to $export (after unescaping), rather than searching the full URL string.

Suggested change
if (requestUrl != null && requestUrl.Contains("$export", StringComparison.OrdinalIgnoreCase))
{
throw new RequestNotValidException(Core.Resources.ExportOperationNotSupportedInBundle);
if (!string.IsNullOrEmpty(requestUrl))
{
Uri exportDetectionUri = new Uri(LocalHost, requestUrl);
string exportDetectionPath = exportDetectionUri.GetComponents(UriComponents.Path, UriFormat.Unescaped);
bool hasExportPathSegment = exportDetectionPath
.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries)
.Any(segment => segment.Equals("$export", StringComparison.OrdinalIgnoreCase));
if (hasExportPathSegment)
{
throw new RequestNotValidException(Core.Resources.ExportOperationNotSupportedInBundle);
}

Copilot uses AI. Check for mistakes.
Comment on lines +545 to +548
if (requestUrl != null && requestUrl.Contains("$export", StringComparison.OrdinalIgnoreCase))
{
throw new RequestNotValidException(Core.Resources.ExportOperationNotSupportedInBundle);
}
Copy link

Copilot AI Mar 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This throws RequestNotValidException with the default issue type (invalid). Since the message explicitly says the operation is not supported (and the linked issue asks for Not Supported), pass OperationOutcomeConstants.IssueType.NotSupported (or use an exception type that sets it) so OperationOutcome.issue.code matches the semantics.

Copilot uses AI. Check for mistakes.
Comment on lines +542 to +548
// Export operations are not supported within bundles (batch or transaction).
// Return a clear error instead of the cryptic Accept header error that occurs
// when the internal routing fails to set required export headers.
if (requestUrl != null && requestUrl.Contains("$export", StringComparison.OrdinalIgnoreCase))
{
throw new RequestNotValidException(Core.Resources.ExportOperationNotSupportedInBundle);
}
Copy link

Copilot AI Mar 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are existing xUnit tests for bundle handling under src/Microsoft.Health.Fhir.Shared.Api.UnitTests/Features/Resources/Bundle/, but this new $export-in-bundle guard is not covered. Add a unit test that submits a batch/transaction bundle containing an entry with a $export URL and asserts the thrown exception/OperationOutcome contains the new resource message (and the intended issue type).

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tranhoangtu-it could you add a unit test for this in BundleHandlerTests.cs?

@LTA-Thinking LTA-Thinking added the External Author PR is written by an author external to Microsoft label Mar 30, 2026
… type

- Split URL on '?' to check path only, avoiding false positive on
  query strings like Patient?name=$export
- Use OperationOutcomeConstants.IssueType.NotSupported per issue
  requirement and FHIR spec for unsupported operations
@tranhoangtu-it
Copy link
Copy Markdown
Author

Addressed Copilot review:

  1. False positive on query strings — Now splits URL on ? and only checks the path component. Patient?name=$export no longer triggers the guard.
  2. Issue type — Changed to OperationOutcomeConstants.IssueType.NotSupported per the issue requirement and FHIR spec (the operation is not supported in this context, not invalid).
  3. Unit test — Will add in follow-up.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 3 changed files in this pull request and generated 3 comments.

Files not reviewed (1)
  • src/Microsoft.Health.Fhir.Core/Resources.Designer.cs: Language not supported

Comment on lines +548 to +549
string path = requestUrl.Split('?')[0];
if (path.Contains("$export", StringComparison.OrdinalIgnoreCase))
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Local variable path declared here conflicts with the existing string path = ... later in GenerateRequest (C# disallows redeclaring a local from an enclosing scope), which will cause a build error (CS0136). Rename this variable (e.g., requestPath) or reuse the later URI-parsed path for the $export check to avoid duplicate parsing.

Suggested change
string path = requestUrl.Split('?')[0];
if (path.Contains("$export", StringComparison.OrdinalIgnoreCase))
string requestPath = requestUrl.Split('?')[0];
if (requestPath.Contains("$export", StringComparison.OrdinalIgnoreCase))

Copilot uses AI. Check for mistakes.
Comment on lines +551 to +554
throw new RequestNotValidException(
Core.Resources.ExportOperationNotSupportedInBundle,
OperationOutcomeConstants.IssueType.NotSupported);
}
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR description states this should return OperationOutcome.issue.code = "invalid", but the code throws RequestNotValidException with IssueType.NotSupported (which maps to not-supported). Either update the PR description/test plan to reflect not-supported, or change the issue type here to Invalid if that’s the intended behavior.

Copilot uses AI. Check for mistakes.
Comment on lines +542 to +555
// Export operations are not supported within bundles (batch or transaction).
// Return a clear error instead of the cryptic Accept header error that occurs
// when the internal routing fails to set required export headers.
// Check the path component only to avoid false positives from query string values.
if (requestUrl != null)
{
string path = requestUrl.Split('?')[0];
if (path.Contains("$export", StringComparison.OrdinalIgnoreCase))
{
throw new RequestNotValidException(
Core.Resources.ExportOperationNotSupportedInBundle,
OperationOutcomeConstants.IssueType.NotSupported);
}
}
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is an existing unit test (BundleHandlerTests.GivenABundleWithAnExportPost_WhenProcessed_ThenItIsProcessedCorrectly) that currently expects /$export inside a batch bundle to succeed. With this new guard, that test will fail and should be updated (and replaced with assertions that a bundle containing $export returns the new ExportOperationNotSupportedInBundle message and not-supported issue type).

Copilot generated this review using guidance from repository custom instructions.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

External Author PR is written by an author external to Microsoft

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Improve error message for bulk export in FHIR batches & transactions

3 participants