Repository navigation
fix(billing-cost-management-mcp-server): complete the error envelope and fix retry classification gaps - #4781
Merged
finklen-amazon merged 5 commits intoOct 15, 2026
Conversation
…ors whose text is only in data
41 format_response('error', ...) sites across 16 tool files pass their text in
data without a message argument, so the error body lacks the top-level message
documented under "Error responses" in the README. format_response now lifts a
string data['message'] to the top-level message (data is unchanged), and the two
bcm-pricing-calc sites that use data['error'] pass message explicitly.
finklen-amazon
requested review from
a team,
Comusus,
Rahul-1404,
anniewn,
bhatia-di,
callnmm,
chittev,
johnwangwyx,
mattkelsey,
shsrams,
somsubhro and
wangyuhere
as code owners
October 15, 2026 15:28
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #4781 +/- ##
==========================================
+ Coverage 93.53% 93.55% +0.01%
==========================================
Files 1068 1068
Lines 92320 92342 +22
Branches 15017 15024 +7
==========================================
+ Hits 86352 86386 +34
+ Misses 3573 3562 -11
+ Partials 2395 2394 -1 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
…id-request errors that defaulted to do_not_retry - budget_tools, storage_lens_tools: stop wrapping ClientError/BotoCoreError in a generic Exception, which hid the AWS code (throttling, 5xx) from handle_aws_error. Athena poll timeout raises TimeoutError (mapped to retry); failed queries, missing manifests and bad table references raise ValueError. - sql_utils: invalid or rejected session SQL is modify_request. - compute_optimizer_automation_tools: when every region fails, combine the per-region guidance (new error_contract.combine_guidance) instead of relying on multi_region_error, which always defaulted to do_not_retry. - compute_optimizer_tools: unsupported-operation and enrollment errors set error_type at the top level; map its throttling_error, service_unavailable, resource_not_found and opt_in_required types. - error_contract: map NotFoundException, ExpiredNextTokenException, InvalidRequestException, InternalErrorException, not_found and unknown_timeouterror. - handle_aws_error: an empty AWS Message falls back to the default text. Existing error_type values are unchanged.
4 of 6 tasks
Comusus
previously approved these changes
Oct 15, 2026
…onformance checks
Scans the package source and fails when a tool error path would silently get
retry_strategy=do_not_retry:
- an error_type literal that is not mapped in error_contract.py (or listed in
UNMAPPED_BY_DESIGN with a reason)
- format_response('error', ...) with error_type only inside data
- Exception/RuntimeError raised in an except block, hiding the AWS error code
Against the code before this PR it flags the compute-optimizer nested error_type
sites and the budget and storage-lens wrapping fixed in the previous commits.
finklen-amazon
enabled auto-merge
October 15, 2026 16:33
finklen-amazon
disabled auto-merge
October 15, 2026 16:56
finklen-amazon
enabled auto-merge
October 15, 2026 17:54
Comusus
approved these changes
Oct 15, 2026
wangyuhere
approved these changes
Oct 15, 2026
4 of 6 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes
Summary
Changes
The README's "Error responses" section documents a top-level
messageon every tool error, but 41format_response('error', ...)sites across 16 tool files put their text only indataand pass nomessage. Clients reading the documented field get nothing for these errors:aws-pricing,bcm-pricing-calc,cost-allocation-tags,cost-category,cost-explorer,credits,enterprise-support,invoice-units,invoicing,procurement-preferences(mostly validation, missing-parameter, and unsupported-operation paths).Fix:
format_response: forstatus='error'with nomessage, a stringdata['message']is also set as the top-levelmessage.datais unchanged, so readers ofdata.messagekeep working. Success responses are untouched.bcm-pricing-calc: the two missing-parameter errors that usedata['error']now passmessageexplicitly. Onlydata['message']is lifted generically.Fixing it in
format_responserather than editing all 41 call sites keeps the diff small, avoids duplicating each message, and covers future sites that follow the same pattern.Errors that defaulted to
do_not_retrywhen a retry or a changed request would succeed.ErrorSignalingMiddlewarederivesretry_strategyfrom the top-levelerror_type, so an error whose AWS code is hidden, or whose type is not mapped, gets the conservative default. Fixed without renaming any existingerror_type:budgets,budget-actions,budget-notifications:get_aws_account_idwrapped STS errors in a plainException, so a throttle becameunknown_exception. The original error now propagates.storage-lens: the same wrapping for S3GetObject/ListObjectsV2and AthenaStartQueryExecutionerrors. They are now re-raised. The Athena poll timeout raisesTimeoutError(mapped asunknown_timeouterrortoretry_same_request), and a failed query, a missing manifest or report file, and a query without a table reference raiseValueError(validation_error).session-sql:error_typeis the exception class (OperationalError,ValueError), too generic to map globally, so invalid or rejected SQL setsretry_strategy='modify_request'at that site.compute-optimizer-automation: when every region fails for different reasons, the type ismulti_region_errorand per-regionhttp_statusstays nested, so throttling in one region plus a 5xx in another wasdo_not_retry.retry_strategyis now combined from each region's guidance by a newerror_contract.combine_guidance(identical guidance is kept with its hint; otherwisedo_not_retry>retry_same_request>modify_request).compute-optimizer: the unsupported-operation and not-enrolled errors haderror_typeonly indata; it is now top level. ItsClientErrorhandler'sthrottling_error,service_unavailable,resource_not_foundandopt_in_requiredare now mapped.NotFoundException,ExpiredNextTokenException,InvalidRequestException(modify),InternalErrorException(retry), plusenterprise-support'snot_found(modify).handle_aws_error: an AWS error with an emptyMessagefalls back to the default text instead of''.Guardrail:
tests/test_error_contract_conformance.py. Static checks over the package source so these gaps fail in CI instead of in production:error_typeis mapped inerror_contract.py, or listed inUNMAPPED_BY_DESIGNwith a reason (each entry is also checked to still be unmapped)format_response('error', ...)witherror_typeonly insidedataException/RuntimeErrorraised in anexceptblock, which hides the AWS error codeRun against the tool code before this PR, it flags the two compute-optimizer nested
error_typesites and the four budget / storage-lens wrapping sites.Out of scope here:
BotoCoreErrorsubclasses such asParamValidationErrorstill map toaws_connection_error(being addressed in #4766), and arguments rejected by FastMCP before the tool runs still have no envelope.User experience
Before, an invalid
billing_periodtoinvoicingreturned:{"status": "error", "data": {"message": "billing_period must be in YYYY-MM format (e.g. \"2026-05\")."}, "error_type": "validation_error", "operation": "ListInvoiceSummaries", "service": "Invoicing", "retry_strategy": "modify_request"}After, the same body plus:
{"message": "billing_period must be in YYYY-MM format (e.g. \"2026-05\")."}Checklist
If your change doesn't seem to apply, please leave them unchecked.
Tests (each fix has a test that fails without it):
tests/utilities/test_aws_service_base.pycovers liftingdata['message'], an explicitmessagewinning,data['error']and non-string or non-dict data not being lifted, and success responses keeping their shape. Theinvoicing,credits, andbcm-pricing-calcvalidation tests now assert the top-levelmessage. Reverting the fix fails the new assertions. Also checked end to end through a FastMCP client forinvoicing,credits, andaws-pricing:isErroris set,messageis top level, and the text content matchesstructured_content. New tests cover the added mappings,combine_guidance, multi-region guidance, session-sql, the storage-lens timeout/failed-query paths and unwrapped re-raises, the STS re-raise, the compute-optimizer top-levelerror_type, and the empty-message fallback. A FastMCP client run confirmssession-sql,compute-optimizerandinvoicingerrors carry the expectederror_type,retry_strategyand top-levelmessage. All 1915 tests pass; ruff check/format and pyright pass.Is this a breaking change? (Y/N)
N. Existing
error_typevalues and fields are unchanged. Error responses gain a top-levelmessagewhere it was missing, and some errors get a more accurateretry_strategy. The wrapped storage-lens and budget errors now report their real AWSerror_typeinstead ofunknown_exception.RFC issue number:
Checklist:
Acknowledgment
By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of the project license.