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
- User has an existing AAP instance in
IDLED state
- User clicks an AAP product card (triggers
handleAAPInstance)
getAAPData fetches fresh data, sets ansibleStatus = IDLED
handleAAPInstance checks ansibleStatus but sees NEW (stale closure)
- 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.
Description
In
useSandboxContext.tsx, thehandleAAPInstancefunction callsawait getAAPData(userNamespace)which updatesansibleStatusandansibleDataviaReact
setState. However, immediately after the await, the function readsansibleStatusand
ansibleDatafrom the outer closure — which still hold the previous render's valuesbecause React state updates are asynchronous and don't mutate the closure variable.
Impact
This can cause incorrect AAP lifecycle operations:
READYNEWcreateAAP()IDLEDNEWcreateAAP()unIdleAAP()PROVISIONINGNEWcreateAAP()The
createAAPcall handles HTTP 409 (conflict) gracefully, which partially masks thisbug — but calling
createAAPinstead ofunIdleAAPfor an idled instance is semanticallywrong and may cause unexpected behavior.
Steps to reproduce
IDLEDstatehandleAAPInstance)getAAPDatafetches fresh data, setsansibleStatus = IDLEDhandleAAPInstancechecksansibleStatusbut seesNEW(stale closure)createAAPis called instead ofunIdleAAPProposed Solution
Have
getAAPDatareturn the computed status and data sohandleAAPInstancecan usefresh values directly instead of relying on the closure:
Files to change
plugins/sandbox/src/hooks/useSandboxContext.tsxI'd be happy to contribute a PR for this if the approach sounds good.