Skip to content

Rust ARM discovery: a non-object value entry creates a phantom project and suppresses the classic-hub fallback#445

Description

馃悰 Bug Description

In runtime/rust/prompty-foundry/src/arm_discovery.rs, fetch_all accumulates every entry of an ARM page's value array, including non-object ones (null, strings, numbers).

list_foundry_projects then maps the modern ("S1") project results with an infallible .map(parse_s1_project) (line 91), whereas the classic-hub ("S2") results use .filter_map(parse_s2_workspace) (line 103).

The consequence is that a single stray non-object entry in the S1 response becomes a FoundryProject with empty fields. Because projects is then non-empty, the if projects.is_empty() guard at line 95 skips the classic-hub fallback entirely - so a caller gets one junk project instead of the real ones the fallback would have found.

Two things go wrong at once: a bogus entry is surfaced, and a legitimate discovery path is suppressed.

Steps to reproduce

  1. Call list_foundry_projects(token, sub, rg, resource) against an account whose projects only exist as classic ML-workspace hubs (so the S1 endpoint has nothing real to return).

  2. Have the S1 Microsoft.CognitiveServices/accounts/{name}/projects endpoint return a payload whose value array contains a non-object entry, e.g.:

    { "value": [null] }
  3. Observe the returned project list.

Expected behavior

The non-object entry is ignored, projects stays empty, the classic-hub fallback runs, and the real projects are returned.

This is what the Java runtime does - its paging helper keeps only object entries, so the fallback stays reachable. Covered by FoundryArmTest.Paging.nonObjectEntriesAreSkipped in runtime/java/prompty-foundry.

Actual behavior

projects contains a single FoundryProject with an empty name, a display_name falling back to the resource name, and an endpoint of the form https://{resource}.services.ai.azure.com/api/projects/ with a trailing slash and no project segment. The classic-hub fallback never runs, so the real projects are never discovered.

Your environment

  • Runtime: runtime/rust/prompty-foundry (arm_discovery.rs)
  • Affected functions: fetch_all (~lines 115-154), list_foundry_projects (~lines 79-109)

Notes for whoever picks this up

  • ARM does not emit non-object value entries in practice, so this is a malformed/hostile-input robustness issue rather than something users hit against a healthy control plane. Filing it because it is a genuine cross-runtime divergence - Rust is the behavioural reference for the other runtimes, and here the reference is the incorrect one, so it should be fixed rather than replicated.
  • Suggested fix: filter fetch_all's accumulation to object entries, or make parse_s1_project fallible and use filter_map for symmetry with parse_s2_workspace. The latter also guards against a well-formed-but-empty object producing the same phantom entry.
  • Related, deliberately not fixed in either runtime yet: neither fetch_all implementation guards against a nextLink that points back at the URL just fetched, so a looping control-plane response would page forever. Both Rust and Java are affected identically. It is a hang in a path a human is usually waiting on (a resource picker), so it is worth a bounded page count or a visited-URL check - but it should land in both runtimes together so a shared bug is not traded for a divergence.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions