User-friendly HTTP error messages for API failures#135
Merged
Conversation
d44375d to
e544da4
Compare
Introduces http_errors.py with centralized error handling that translates raw HTTP status codes into actionable messages with troubleshooting tips. Status code coverage: - 400: version mismatch / bad query guidance - 401: re-sign-in prompt - 403: permission denied with specific role suggestions - 404: resource/solution not deployed - 429: rate limiting with wait guidance - 5xx: transient server error with retry advice Changes: - New: scripts/http_errors.py (APIError class, raise_api_error helper) - auth.py: replace bare raise_for_status() with raise_api_error() - discover.py: catch APIError and show friendly terminal output - fetch_and_setup.py: catch APIError in both refresh and normal modes The APIError class subclasses requests.HTTPError for backward compatibility with existing except-HTTPError handlers. Entity set names are mapped to friendly labels (bots -> agents, workflows -> cloud flows, etc.) and messages are operation-aware (read/create/ update/delete). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
e544da4 to
e75a3e4
Compare
qazizaahirah
reviewed
Jun 9, 2026
qazizaahirah
reviewed
Jun 9, 2026
1. AuthExpiredError now subclasses APIError (qazizaahirah comment 1). Previously 401 raised AuthExpiredError(RuntimeError), which escaped the friendly handler in discover.py / fetch_and_setup.py since those only catch APIError. Now `except APIError` catches 401s too and renders the same friendly message, while `except AuthExpiredError` in push.py / FlightCheck still works for re-auth-and-retry. All 6 raise sites in auth.py pass response=resp so 401s carry the same URL / method / request-id diagnostics as other API errors. The 401 friendly message includes `HTTP 401` so existing test_preferred_solution.py assertions keep passing. 2. format_for_terminal now reads response.request.method directly instead of reverse-engineering the verb from the operation string (qazizaahirah comment 3). Removes a 4-branch if/elif chain. 3. Add tests/scripts/test_http_errors.py with 41 tests covering each status code, the entity-name mapping, format_for_terminal output, raise_api_error behavior, and the AuthExpiredError subclassing contract (qazizaahirah comment 2). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
qazizaahirah
approved these changes
Jun 10, 2026
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.
Summary
ADO tasks https://o365exchange.visualstudio.com/O365%20Core/_workitems/edit/7469877 and https://o365exchange.visualstudio.com/O365%20Core/_workitems/edit/7469904
Adds a centralized HTTP error handler that translates raw status codes into actionable messages with troubleshooting tips. This significantly improves the experience when users hit permission or connectivity issues during install/setup.
Problem
When API calls fail (e.g., user lacks Dataverse read permission), the scripts showed raw tracebacks like:
requests.exceptions.HTTPError: 403 Client Error: Forbidden for url: https://org.crm.dynamics.com/api/data/v9.2/bots?=...Non-technical users don't know what a 403 means or how to fix it.
Solution
New
scripts/http_errors.pymodule with:APIErrorclass (subclassesrequests.HTTPErrorfor backward compat)raise_api_error(response, resource_name, operation, required_role)helperbots→ "agents",workflows→ "cloud flows")Example output (403 on bots query):
ERROR: You signed in successfully, but your account doesn't have permission to read agents. Tip: Ask your Power Platform admin to assign the Bot Author, Bot Contributor, or System Administrator security role to your account in this environment. Detail: HTTP 403 — GET https://org.crm.dynamics.com/api/data/v9.2/botsStatus codes covered:
Files changed
scripts/http_errors.py— central error handlerscripts/auth.py— replaceraise_for_status()withraise_api_error()scripts/discover.py— catchAPIError, show friendly outputscripts/fetch_and_setup.py— catchAPIErrorin refresh + normal modesDesign decisions
_errordictssync_docs.py/sync_samples.pyNOT changed — internal tooling, not user-facing during installAuthExpiredError(callers depend on it for re-auth)APIErrorsubclassesHTTPErrorso existingexcept HTTPErrorhandlers still workTesting
Verified via unit mock: all status codes produce correct messages, 2xx doesn't raise,
except HTTPErrorstill catchesAPIError.