Skip to content

Bug: handleAAPInstance reads stale React state after async getAAPData call #62

@Atharva0506

Description

@Atharva0506

Description

In useSandboxContext.tsx, the handleAAPInstance function calls
await getAAPData(userNamespace) which updates ansibleStatus and ansibleData via
React setState. However, immediately after the await, the function reads ansibleStatus
and ansibleData from the outer closure — which still hold the previous render's values
because React state updates are asynchronous and don't mutate the closure variable.

Impact

This can cause incorrect AAP lifecycle operations:

Actual status after fetch Closure value (stale) What happens What should happen
READY NEW Calls createAAP() Should return early
IDLED NEW Calls createAAP() Should call unIdleAAP()
PROVISIONING NEW Calls createAAP() Should return early

The createAAP call handles HTTP 409 (conflict) gracefully, which partially masks this
bug — but calling createAAP instead of unIdleAAP for an idled instance is semantically
wrong and may cause unexpected behavior.

Steps to reproduce

  1. User has an existing AAP instance in IDLED state
  2. User clicks an AAP product card (triggers handleAAPInstance)
  3. getAAPData fetches fresh data, sets ansibleStatus = IDLED
  4. handleAAPInstance checks ansibleStatus but sees NEW (stale closure)
  5. IDLED branch is skipped → createAAP is called instead of unIdleAAP

Proposed Solution

Have getAAPData return the computed status and data so handleAAPInstance can use
fresh values directly instead of relying on the closure:

const getAAPData = async (userNamespace: string) => {
    // ... existing fetch and setState logic ...
    return { status: st, data };
};

const handleAAPInstance = async (userNamespace: string) => {
    const { status, data } = await getAAPData(userNamespace);
    
    if (status === AnsibleStatus.PROVISIONING || status === AnsibleStatus.READY) {
      return;
    }
    
    if (status === AnsibleStatus.IDLED && data && data?.items?.length > 0) {
      await aapApi.unIdleAAP(userNamespace);
      return;
    }
    
    await aapApi.createAAP(userNamespace);
};

Files to change

plugins/sandbox/src/hooks/useSandboxContext.tsx

I'd be happy to contribute a PR for this if the approach sounds good.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions