Skip to content

{Core} Adjust "command not found" scenarios to fail fast. #32826

Closed
DanielMicrosoft wants to merge 5 commits intoAzure:devfrom
DanielMicrosoft:36654220-command-not-found-fail-fast
Closed

{Core} Adjust "command not found" scenarios to fail fast. #32826
DanielMicrosoft wants to merge 5 commits intoAzure:devfrom
DanielMicrosoft:36654220-command-not-found-fail-fast

Conversation

@DanielMicrosoft
Copy link
Copy Markdown
Contributor

@DanielMicrosoft DanielMicrosoft commented Feb 23, 2026

Related command

Description

This PR prevents azure-cli from doing a full module load in the event that a command is not found.

The implementation of this feature will require altering of when the commandIndex is re-built.

It will also require a change to workflows in air-gapped environments. Where developers may manually install whl files, as we will rely on rebuilding commandIndex at the "end" of an operation that should normally require a rebuild on the next command used (the extension "CRUD" operations). This PR implements a manual way (via a newly created command) to refresh the commandIndex.

Checklist:

  • Fresh install: cache is "invalid" and trigger rebuild
  • Version upgrade: cache is invalid and trigger rebuild.
  • Add extension: rebuild is triggered at the "end" of the code path that adds extension, meaning the next command after that will not need to trigger it.
  • Update extension: rebuild is triggered at the "end" of the code path that adds extension, meaning the next command after that will not need to trigger it.
  • Remove extension: rebuild is triggered at the "end" of the code path that adds extension, meaning the next command after that will not need to trigger it.
  • Will scenarios where user does not use commandIndex be covered (should just always go to fallback of loading all modules).
  • CommandIndex enabled: test new rebuild command
  • CommandIndex disabled: test new rebuild command (it rebuilds as normal, wondering should we give the user a warning that they are not using commandIndex in their config?).
  • Case-insensitivity is preserved at top-level.
  • Test air-gapped env with whl files installed via azure-cli
  • Test air-gapped env with manually installed extensions
  • Update air-gap development docs.
  • Test with dynamic install on/off
  • Investigate why manual rebuild command appears to take longer than normal paths that trigger a rebuild (ie az VERSION).
  • Ensure searchable/distinct telemetry params are set for new command. ("Context.Default.AzureCLI.RawCommand": "rebuild-index").
  • Automated testing for rebuilding commandIndex after extension operations.

Testing Guide

History Notes

[Component Name 1] BREAKING CHANGE: az command a: Make some customer-facing breaking change
[Component Name 2] az command b: Add some customer-facing feature


This checklist is used to make sure that common guidelines for a pull request are followed.

@azure-client-tools-bot-prd
Copy link
Copy Markdown

azure-client-tools-bot-prd bot commented Feb 23, 2026

❌AzureCLI-FullTest
️✔️acr
️✔️latest
️✔️3.12
️✔️3.13
️✔️acs
️✔️latest
️✔️3.12
️✔️3.13
️✔️advisor
️✔️latest
️✔️3.12
️✔️3.13
️✔️ams
️✔️latest
️✔️3.12
️✔️3.13
️✔️apim
️✔️latest
️✔️3.12
️✔️3.13
️✔️appconfig
️✔️latest
️✔️3.12
️✔️3.13
️✔️appservice
️✔️latest
️✔️3.12
️✔️3.13
️✔️aro
️✔️latest
️✔️3.12
️✔️3.13
️✔️backup
️✔️latest
️✔️3.12
️✔️3.13
️✔️batch
️✔️latest
️✔️3.12
️✔️3.13
️✔️batchai
️✔️latest
️✔️3.12
️✔️3.13
️✔️billing
️✔️latest
️✔️3.12
️✔️3.13
️✔️botservice
️✔️latest
️✔️3.12
️✔️3.13
️✔️cdn
️✔️latest
️✔️3.12
️✔️3.13
️✔️cloud
️✔️latest
️✔️3.12
️✔️3.13
️✔️cognitiveservices
️✔️latest
️✔️3.12
️✔️3.13
️✔️compute_recommender
️✔️latest
️✔️3.12
️✔️3.13
️✔️computefleet
️✔️latest
️✔️3.12
️✔️3.13
️✔️config
️✔️latest
️✔️3.12
️✔️3.13
️✔️configure
️✔️latest
️✔️3.12
️✔️3.13
️✔️consumption
️✔️latest
️✔️3.12
️✔️3.13
️✔️container
️✔️latest
️✔️3.12
️✔️3.13
️✔️containerapp
️✔️latest
️✔️3.12
️✔️3.13
❌core
❌latest
❌3.12
Type Test Case Error Message Line
Failed test_run_az_cmd self = <azure.cli.core.tests.test_util.TestUtils testMethod=test_run_az_cmd>

    def test_run_az_cmd(self):
        cmd = ["az", "version"]
>       output = run_az_cmd(cmd)
                 ^^^^^^^^^^^^^^^

src/azure-cli-core/azure/cli/core/tests/test_util.py:447: 
                                        
src/azure-cli-core/azure/cli/core/util.py:1486: in run_az_cmd
    cli.invoke(args, out_file=out_file)
env/lib/python3.12/site-packages/knack/cli.py:250: in invoke
    raise ex
env/lib/python3.12/site-packages/knack/cli.py:233: in invoke
    cmd_result = self.invocation.execute(args)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
src/azure-cli-core/azure/cli/core/commands/init.py:606: in execute
    parsed_args = self.parser.parse_args(args)
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
env/lib/python3.12/site-packages/knack/parser.py:261: in parse_args
    return super().parse_args(args)
           ^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.12.12/x64/lib/python3.12/argparse.py:1904: in parse_args
    args, argv = self.parse_known_args(args, namespace)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
src/azure-cli-core/azure/cli/core/parser.py:282: in parse_known_args
    self.namespace, self.raw_arguments = super().parse_known_args(args=args, namespace=namespace)
                                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.12.12/x64/lib/python3.12/argparse.py:1914: in parse_known_args
    return self.parse_known_args2(args, namespace, intermixed=False)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.12.12/x64/lib/python3.12/argparse.py:1943: in parse_known_args2
    namespace, args = self.parse_known_args(args, namespace, intermixed)
                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.12.12/x64/lib/python3.12/argparse.py:2188: in parse_known_args
    stop_index = consume_positionals(start_index)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.12.12/x64/lib/python3.12/argparse.py:2141: in consume_positionals
    take_action(action, args)
/opt/hostedtoolcache/Python/3.12.12/x64/lib/python3.12/argparse.py:2003: in take_action
    argument_values = self.get_values(action, argument_strings)
                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
src/azure-cli-core/azure/cli/core/parser.py:273: in get_values
    value = super().get_values(action, arg_strings)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.12.12/x64/lib/python3.12/argparse.py:2523: in get_values
    self.check_value(action, value[0])
src/azure-cli-core/azure/cli/core/parser.py:338: in check_value
    self.exit(2)
 
 
 
 
 
 
 
 
 
 
 
                             

self = AzCliCommandParser(prog='az', usage=None, description=None, formatter_class=<class 'argparse.HelpFormatter'>, conflict_handler='error', add_help=True)
status = 2, message = None

    def exit(self, status=0, message=None):
        if message:
            self._print_message(message, _sys.stderr)
>       _sys.exit(status)
E       SystemExit: 2

/opt/hostedtoolcache/Python/3.12.12/x64/lib/python3.12/argparse.py:2637: SystemExit
azure/cli/core/tests/test_util.py:444
❌3.13
Type Test Case Error Message Line
Failed test_run_az_cmd self = <azure.cli.core.tests.test_util.TestUtils testMethod=test_run_az_cmd>

    def test_run_az_cmd(self):
        cmd = ["az", "version"]
>       output = run_az_cmd(cmd)
                 ^^^^^^^^^^^^^^^

src/azure-cli-core/azure/cli/core/tests/test_util.py:447: 
                                        
src/azure-cli-core/azure/cli/core/util.py:1486: in run_az_cmd
    cli.invoke(args, out_file=out_file)
env/lib/python3.13/site-packages/knack/cli.py:250: in invoke
    raise ex
env/lib/python3.13/site-packages/knack/cli.py:233: in invoke
    cmd_result = self.invocation.execute(args)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
src/azure-cli-core/azure/cli/core/commands/init.py:606: in execute
    parsed_args = self.parser.parse_args(args)
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
env/lib/python3.13/site-packages/knack/parser.py:261: in parse_args
    return super().parse_args(args)
           ^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.13.11/x64/lib/python3.13/argparse.py:1898: in parse_args
    args, argv = self.parse_known_args(args, namespace)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
src/azure-cli-core/azure/cli/core/parser.py:282: in parse_known_args
    self.namespace, self.raw_arguments = super().parse_known_args(args=args, namespace=namespace)
                                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.13.11/x64/lib/python3.13/argparse.py:1908: in parse_known_args
    return self.parse_known_args2(args, namespace, intermixed=False)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.13.11/x64/lib/python3.13/argparse.py:1937: in parse_known_args2
    namespace, args = self.parse_known_args(args, namespace, intermixed)
                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.13.11/x64/lib/python3.13/argparse.py:2192: in parse_known_args
    stop_index = consume_positionals(start_index)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.13.11/x64/lib/python3.13/argparse.py:2144: in consume_positionals
    take_action(action, args)
/opt/hostedtoolcache/Python/3.13.11/x64/lib/python3.13/argparse.py:1998: in take_action
    argument_values = self.get_values(action, argument_strings)
                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
src/azure-cli-core/azure/cli/core/parser.py:273: in get_values
    value = super().get_values(action, arg_strings)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.13.11/x64/lib/python3.13/argparse.py:2528: in get_values
    self.check_value(action, value[0])
src/azure-cli-core/azure/cli/core/parser.py:338: in check_value
    self.exit(2)
 
 
 
 
 
 
 
 
 
 
 
                             

self = AzCliCommandParser(prog='az', usage=None, description=None, formatter_class=<class 'argparse.HelpFormatter'>, conflict_handler='error', add_help=True)
status = 2, message = None

    def exit(self, status=0, message=None):
        if message:
            self._print_message(message, _sys.stderr)
>       _sys.exit(status)
E       SystemExit: 2

/opt/hostedtoolcache/Python/3.13.11/x64/lib/python3.13/argparse.py:2645: SystemExit
azure/cli/core/tests/test_util.py:444
️✔️cosmosdb
️✔️latest
️✔️3.12
️✔️3.13
️✔️databoxedge
️✔️latest
️✔️3.12
️✔️3.13
️✔️dls
️✔️latest
️✔️3.12
️✔️3.13
️✔️dms
️✔️latest
️✔️3.12
️✔️3.13
️✔️eventgrid
️✔️latest
️✔️3.12
️✔️3.13
️✔️eventhubs
️✔️latest
️✔️3.12
️✔️3.13
️✔️feedback
️✔️latest
️✔️3.12
️✔️3.13
️✔️find
️✔️latest
️✔️3.12
️✔️3.13
️✔️hdinsight
️✔️latest
️✔️3.12
️✔️3.13
️✔️identity
️✔️latest
️✔️3.12
️✔️3.13
️✔️iot
️✔️latest
️✔️3.12
️✔️3.13
️✔️keyvault
️✔️latest
️✔️3.12
️✔️3.13
️✔️lab
️✔️latest
️✔️3.12
️✔️3.13
️✔️managedservices
️✔️latest
️✔️3.12
️✔️3.13
️✔️maps
️✔️latest
️✔️3.12
️✔️3.13
️✔️marketplaceordering
️✔️latest
️✔️3.12
️✔️3.13
️✔️monitor
️✔️latest
️✔️3.12
️✔️3.13
️✔️mysql
️✔️latest
️✔️3.12
️✔️3.13
️✔️netappfiles
️✔️latest
️✔️3.12
️✔️3.13
️✔️network
️✔️latest
️✔️3.12
️✔️3.13
️✔️policyinsights
️✔️latest
️✔️3.12
️✔️3.13
️✔️postgresql
️✔️latest
️✔️3.12
️✔️3.13
️✔️privatedns
️✔️latest
️✔️3.12
️✔️3.13
️✔️profile
️✔️latest
️✔️3.12
️✔️3.13
️✔️rdbms
️✔️latest
️✔️3.12
️✔️3.13
️✔️redis
️✔️latest
️✔️3.12
️✔️3.13
️✔️relay
️✔️latest
️✔️3.12
️✔️3.13
️✔️resource
️✔️latest
️✔️3.12
️✔️3.13
️✔️role
️✔️latest
️✔️3.12
️✔️3.13
️✔️search
️✔️latest
️✔️3.12
️✔️3.13
️✔️security
️✔️latest
️✔️3.12
️✔️3.13
️✔️servicebus
️✔️latest
️✔️3.12
️✔️3.13
️✔️serviceconnector
️✔️latest
️✔️3.12
️✔️3.13
️✔️servicefabric
️✔️latest
️✔️3.12
️✔️3.13
️✔️signalr
️✔️latest
️✔️3.12
️✔️3.13
️✔️sql
️✔️latest
️✔️3.12
️✔️3.13
️✔️sqlvm
️✔️latest
️✔️3.12
️✔️3.13
️✔️storage
️✔️latest
️✔️3.12
️✔️3.13
️✔️synapse
️✔️latest
️✔️3.12
️✔️3.13
️✔️telemetry
️✔️latest
️✔️3.12
️✔️3.13
️✔️util
️✔️latest
️✔️3.12
️✔️3.13
️✔️vm
️✔️latest
️✔️3.12
️✔️3.13

@azure-client-tools-bot-prd
Copy link
Copy Markdown

Hi @DanielMicrosoft,
Since the current milestone time is less than 7 days, this pr will be reviewed in the next milestone.

@yonzhan
Copy link
Copy Markdown
Collaborator

yonzhan commented Feb 23, 2026

Thank you for your contribution! We will review the pull request and get back to you soon.

@azure-client-tools-bot-prd
Copy link
Copy Markdown

azure-client-tools-bot-prd bot commented Feb 23, 2026

⚠️AzureCLI-BreakingChangeTest
⚠️util
rule cmd_name rule_message suggest_message
⚠️ 1001 - CmdAdd rebuild-index cmd rebuild-index added

@github-actions
Copy link
Copy Markdown

The git hooks are available for azure-cli and azure-cli-extensions repos. They could help you run required checks before creating the PR.

Please sync the latest code with latest dev branch (for azure-cli) or main branch (for azure-cli-extensions).
After that please run the following commands to enable git hooks:

pip install azdev --upgrade
azdev setup -c <your azure-cli repo path> -r <your azure-cli-extensions repo path>

@DanielMicrosoft DanielMicrosoft force-pushed the 36654220-command-not-found-fail-fast branch from f4ac334 to b1b7fa4 Compare February 26, 2026 04:12
@DanielMicrosoft DanielMicrosoft deleted the 36654220-command-not-found-fail-fast branch April 1, 2026 23:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants