Skip to content

[cognitiveservices] Unblock managed compute creation - fix misleading 202 / AuthorizationFailed errors #48096

Open
swarupecenits wants to merge 5 commits into
Azure:mainfrom
swarupecenits:fix/cognitiveservices-compute-create-no-poll
Open

[cognitiveservices] Unblock managed compute creation - fix misleading 202 / AuthorizationFailed errors #48096
swarupecenits wants to merge 5 commits into
Azure:mainfrom
swarupecenits:fix/cognitiveservices-compute-create-no-poll

Conversation

@swarupecenits

@swarupecenits swarupecenits commented Jul 16, 2026

Copy link
Copy Markdown
Member

Description

Fixes the misleading errors users hit when creating a Cognitive Services managed compute via
ComputesOperations.begin_create_or_update (surfaced through az cognitiveservices account compute create).

The generated SDK failed asynchronous compute creation in two ways:

  1. Rejected the async 202 Accepted. The service returns HTTP 202 for the async create, but the
    generated _create_or_update_initial only accepts 200/201, so it raised
    Operation returned an invalid status 'Accepted'.
  2. Polled a permission-gated endpoint. The default poller followed the Azure-AsyncOperation header
    to .../locations/{location}/computeOperations/{operationId}, which requires
    Microsoft.CognitiveServices/locations/computeOperations/read. Callers allowed to create a compute
    but not granted that read permission saw a misleading AuthorizationFailed even though the create
    succeeded.

What this change does

Overrides ComputesOperations.begin_create_or_update (sync + async, in _patch.py) to:

  • Accept 202 in addition to 200/201 (in a minimal _create_or_update_initial override that is
    otherwise identical to the generated method).
  • Track the long-running operation by reading provisioningState from the list API via a custom
    blocking poller (_ComputeListPolling / _AsyncComputeListPolling), matching the compute by name.
    This needs only computes/read (which callers already have) and never touches the
    computeOperations operation-status endpoint
    .
  • Block until the compute reaches a terminal state and, on failure, surface the compute's own
    properties.errors
    (e.g. the quota message) instead of a generic
    Operation returned an invalid status 'OK'.
  • Preserve the generated method's public shape: the three @overloads, the @api_version_validation
    guard, the polling=False escape hatch, and the continuation_token resume path (which does not
    re-issue the create PUT). Genuine non-2xx create failures still propagate.

Why list and not a resource get

The compute create is asynchronous, and a GET on the just-created compute returns
404 "Cluster not found" for an extended, variable period while the cluster backend materializes — long
enough that a get-based poller wrongly fails a create that is actually succeeding. The compute does,
however, appear in the list response with its provisioningState from the moment the create is
accepted. Polling list therefore reflects the true state throughout provisioning. This also matches the
work item's guidance to "rely on the list API for status polling of compute creation."

Only private helpers were added (_ComputeListPolling, _AsyncComputeListPolling, _provisioning_error,
_state_of, _TERMINAL_FAILED_STATES, _TERMINAL_SUCCESS_STATES); the public API surface is
unchanged
(verified: the three public overloads are preserved and api-version validation still fires).

Notes

  • This is a client-side workaround while the service-side polling API is fixed; it lives in _patch.py
    so the code generator does not overwrite it.
  • _patch.py re-implements the initial request send only because the generated _create_or_update_initial
    rejects the 202; it is kept intentionally close to the generated code.
  • The poller blocks until terminal (standard LRO poller.result() semantics) rather than returning early,
    and surfaces provisioning failures - so callers get correct results, not a premature "Accepted".
  • Version bumped 15.0.0b315.0.0b4; CHANGELOG updated.

Testing

  • 22 unit tests (tests/test_computes_operations_patch.py + ..._async.py, 11 sync + 11 async):
    accept 202 · reads status via list, never computeOperations · blocks until terminal · tolerates the
    compute being briefly absent from list then appearing · gives up (bounded) if it never appears ·
    surfaces the real failure reason · propagates non-2xx · polling=False · public overloads preserved ·
    old api-version rejected · continuation_token does not re-send the create. All pass; black +
    pyflakes clean.
  • Live-verified against a real account (jayant-westcentralus, Standard_D64_v3):
    • Success path: create accepted (202), poller blocked through ~33 min of provisioning reading
      provisioningState from list (never a 404), and returned the compute with
      provisioningState: Succeeded.
    • Failure path: on an over-quota create, the poller blocked to terminal and raised the service's
      real error (ClusterCoreQuotaReached quota message) instead of a generic status error.
    • Invalid / non-full-node SKUs are rejected up front, and no AuthorizationFailed /
      invalid status 'Accepted' occurs.

All SDK Contribution checklist:

  • The pull request does not introduce [breaking changes]
  • CHANGELOG is updated for new features, bug fixes or other significant changes.
  • I have read the contribution guidelines.

General Guidelines and Best Practices

  • Title of the pull request is clear and informative.
  • There are a small number of commits, each of which have an informative message.

Testing Guidelines

  • Pull request includes test coverage for the included changes.

Verification

**Unit tests - all passing. **
test_computes_operations_patch.py (sync) and test_computes_operations_patch_async.py (async) cover accepting the async 202, polling the compute resource instead of computeOperations, blocking until terminal state, tolerating the read-after-write 404 window, surfacing the real provisioning failure, propagating non-2xx errors, and the polling=False escape hatch.


Screenshot 2026-07-17 182240

Live end-to-end success. begin_create_or_update(...).result() accepts the async 202, tracks
status via the list API (STEP 2: computeOperations never called), blocks through ~33 min of
provisioning, and returns the created Standard_D64_v3 compute with provisioningState: Succeeded

  • no AuthorizationFailed, no invalid status 'Accepted', and none of the 404 "Cluster not found"
    that a get-based poller hits during creation.

Screenshot 2026-07-17 170329 Screenshot 2026-07-17 170509

…lerate read-after-write 404

Override begin_create_or_update (sync + async) to accept the service's HTTP 202 and track the LRO by polling the compute resource (BodyContentPolling) instead of the computeOperations status endpoint that needs computeOperations/read many callers lack. Tolerates the ~15-20s read-after-write 404 window, blocks until terminal, and surfaces the compute's own provisioning error. Adds 14 unit tests; bumps to 15.0.0b4.
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 1 pipeline(s).
9 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.

This comment was marked as resolved.

@swarupecenits swarupecenits changed the title  [cognitiveservices] Unblock managed compute creation — fix misleading 202 / AuthorizationFailed errors (Task 5441745) [cognitiveservices] Unblock managed compute creation — fix misleading 202 / AuthorizationFailed errors (Task 5441745) Jul 16, 2026
…, no computeOperations)

Poll provisioningState from the list API instead of a resource GET: a GET on a just-created compute returns 404 'Cluster not found' for an extended period while it provisions, whereas list reflects state from the moment the create is accepted. Keeps the 202 accept, blocks until terminal via a custom _ComputeListPolling, surfaces the compute's real error on failure, and preserves the public overloads, api-version validation and continuation_token resume path. Updates 22 unit tests and the CHANGELOG.
@swarupecenits
swarupecenits marked this pull request as ready for review July 17, 2026 11:43
Copilot AI review requested due to automatic review settings July 17, 2026 11:43
@swarupecenits
swarupecenits requested a review from msyyc as a code owner July 17, 2026 11:43
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 1 pipeline(s).
9 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.

This comment was marked as resolved.

Copilot AI review requested due to automatic review settings July 17, 2026 12:07

This comment was marked as resolved.

…idate continuation_token

- Apply caller-supplied cls to the final resource in the list-based poller.
- Preserve terminal Canceled status instead of collapsing it to Failed.
- Encode/decode continuation_token so resume uses the token as source of truth and raises on a malformed token.
- Add sync+async tests for all three (30 tests total).
Copilot AI review requested due to automatic review settings July 17, 2026 12:55

This comment was marked as resolved.

Copilot AI review requested due to automatic review settings July 17, 2026 13:21

This comment was marked as resolved.

@swarupecenits swarupecenits changed the title [cognitiveservices] Unblock managed compute creation — fix misleading 202 / AuthorizationFailed errors (Task 5441745) [cognitiveservices] Unblock managed compute creation - fix misleading 202 / AuthorizationFailed errors Jul 17, 2026
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