Skip to content

Conversation

@AndrewAsseily
Copy link
Contributor

@AndrewAsseily AndrewAsseily commented Dec 2, 2025

Issue #, if available: CLI-5136 & CLI-7572 & #2688

Description of changes:

Exposes modeled error members from AWS service exceptions to stdout in the configured output format (json/yaml/text/table/off), while maintaining the existing formatted error message on stderr.

Behavior:

Command: aws s3api get-object --bucket not-a-real-bucket-0000 --key file.txt out.txt

Before:

An error occurred (NoSuchBucket) when calling the GetObject operation: The specified bucket does not exist

After (stdout):

{
    "Code": "NoSuchBucket",
    "Message": "The specified bucket does not exist",
    "BucketName": "not-a-real-bucket-0000"
}

After (stderr):

An error occurred (NoSuchBucket) when calling the GetObject operation: The specified bucket does not exist

Configuration:

Users can opt out via:

  • Config: cli_error_format = LEGACY
  • Environment: AWS_CLI_ERROR_FORMAT=LEGACY

Additional Changes:

  • Added off as a valid output format option to suppress all output

Description of tests:

  • Unit and functional tests pass locally.

@AndrewAsseily AndrewAsseily changed the base branch from nyandrew/str-std-error to feature/str-std-error December 3, 2025 20:33
Comment on lines +1 to +5
{
"type": "feature",
"category": "Output",
"description": "AWS service errors containing modeled fields beyond Code and Message now write structured output to stdout in the configured output format. Set cli_error_format=LEGACY to disable or use --output off to suppress stdout."
}
Copy link
Member

Choose a reason for hiding this comment

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

--output off is substantial, please add its own entry in a separate file.

Copy link
Member

Choose a reason for hiding this comment

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

Don't need to see changes, but in hindsight I'd rather have done --output off in a separate PR, these features aren't dependent on one another technically.


* yaml-stream

* off
Copy link
Member

Choose a reason for hiding this comment

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

While we're here, can you catch up the section in the API reference too?

The valid values of the ``output`` configuration variable are:
* json
* table
* text

Copy link
Member

Choose a reason for hiding this comment

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

Also add new information there on the new error format setting.

@@ -0,0 +1,94 @@
# Copyright 2012-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
Copy link
Member

Choose a reason for hiding this comment

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

nit: Throughout, either drop years, or use the short version:

# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0

Comment on lines +1096 to +1098
except ClientError as e:
self._display_structured_error_for_exception(e, parsed_globals)
raise
Copy link
Member

Choose a reason for hiding this comment

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

Why do we need this block? Can the formatter raise a structured error that the actual API call wouldn't have?

config_var_name='cli_error_format',
session=self.session,
),
ConstantProvider(value='STANDARD'),
Copy link
Member

Choose a reason for hiding this comment

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

Few things on the enum values:

  1. Left a comment elsewhere, but please start by documenting valid values in global_options.rst
  2. Add validation like we do for output for invalid values.
  3. Only STANDARD | LEGACY were never discussed as options, we should revisit with the team internally.

LOG = logging.getLogger('awscli.structured_error')


class StructuredErrorHandler:
Copy link
Member

Choose a reason for hiding this comment

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

Can this be handled through the existing ClientErrorHandler? That already has an overridable method with access to stdout/stderr.

We'd need less new plumbing in the driver, and we already should have confidence that it is catching ClientError instances.

I also wonder if that'll help apply more of this to custom commands.

try:
formatter = get_formatter(output, parsed_globals)

with self._output_stream_factory.get_output_stream() as stream:
Copy link
Member

Choose a reason for hiding this comment

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

  1. In the last internal review of the design doc, we weren't keen anymore on writing these to stdout. Users checking for any output and assuming that means the command was successful may be broken.
  2. I definitely don't think we should feed errors into the pager. It's odd to need to q to see the original message. Though they can contain lists, there is no mechanism for pagination.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants