Skip to content

Commit f34414d

Browse files
committed
fix: catch unexpected exceptions in child template expansion gracefully
An unexpected error from expand_language_extensions on a child template should not abort packaging of the entire parent stack. The fallback (using original template) is always safe since CFN handles expansion server-side. Now catches Exception with WARNING log as a second handler after the specific InvalidSamDocumentException catch.
1 parent 578b2f2 commit f34414d

2 files changed

Lines changed: 19 additions & 4 deletions

File tree

samcli/lib/package/artifact_exporter.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,9 @@ def do_export(self, resource_id, resource_dict, parent_dir):
117117
else:
118118
LOG.debug("Language extensions expansion failed for %s, using original template", abs_template_path)
119119
result = None
120+
except Exception as e:
121+
LOG.warning("Unexpected error during language extensions expansion for %s: %s", abs_template_path, e)
122+
result = None
120123

121124
if result and result.had_language_extensions:
122125
LOG.debug("Child template %s uses language extensions, expanding before export", abs_template_path)

tests/unit/lib/package/test_artifact_exporter.py

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2529,15 +2529,25 @@ def test_export_cloudformation_stack_language_extensions_expansion_failure(self,
25292529
os.remove(template_path)
25302530

25312531
@patch("samcli.lib.package.artifact_exporter.Template")
2532-
def test_export_cloudformation_stack_unexpected_exception_propagates(self, TemplateMock):
2532+
def test_export_cloudformation_stack_unexpected_exception_falls_back(self, TemplateMock):
25332533
"""
25342534
When expand_language_extensions raises an unexpected exception (not InvalidSamDocumentException),
2535-
it should propagate instead of being silently swallowed.
2535+
it should fall back gracefully to the non-extension flow (not abort the parent packaging).
25362536
"""
25372537
stack_resource = CloudFormationStackResource(self.uploaders_mock, self.code_signer_mock)
25382538

25392539
resource_id = "NestedStack"
25402540
property_name = stack_resource.PROPERTY_NAME
2541+
result_s3_url = "s3://hello/world"
2542+
result_path_style_s3_url = "http://s3.amazonws.com/hello/world"
2543+
exported_template_dict = {"Resources": {}}
2544+
2545+
template_instance_mock = Mock()
2546+
TemplateMock.return_value = template_instance_mock
2547+
template_instance_mock.export.return_value = exported_template_dict
2548+
2549+
self.s3_uploader_mock.upload.return_value = result_s3_url
2550+
self.s3_uploader_mock.to_path_style_s3_url.return_value = result_path_style_s3_url
25412551

25422552
handle = tempfile.NamedTemporaryFile(suffix=".yaml", mode="w", delete=False)
25432553
try:
@@ -2552,7 +2562,9 @@ def test_export_cloudformation_stack_unexpected_exception_propagates(self, Templ
25522562
"samcli.lib.cfn_language_extensions.sam_integration.expand_language_extensions",
25532563
side_effect=TypeError("unexpected bug"),
25542564
):
2555-
with self.assertRaises(ExportFailedError):
2556-
stack_resource.export(resource_id, resource_dict, parent_dir)
2565+
stack_resource.export(resource_id, resource_dict, parent_dir)
2566+
2567+
# Should fall back to non-extension flow successfully
2568+
self.assertEqual(resource_dict[property_name], result_path_style_s3_url)
25572569
finally:
25582570
os.remove(template_path)

0 commit comments

Comments
 (0)