Pre-existing, found while threading the display locale through the same useMemo for #4272. Filed rather than fixed — out of that card's ruled surface.
The gap
packages/plugin-gantt/src/ObjectGantt.tsx — the tasks memo formats every tooltip value eagerly inside its callback, including currency:
case 'currency':
return formatCurrency(Number(value), resolveFieldCurrency(def as any, tenantCurrency));
but tenantCurrency (from useLocalization()) is not in the memo's dependency array, which lists only [data, ganttConfig, objectSchema].
Why it can be observed
useLocalization() is fed by GET /api/v1/auth/me/localization, which is cosmetic and non-blocking — it resolves after first paint. So the first tasks computation runs with tenantCurrency still undefined, and when the tenant's real currency arrives none of data / ganttConfig / objectSchema has changed, the memo does not recompute, and the tooltip keeps the fallback currency until something unrelated invalidates it.
This is the same class of bug as the locale one #4272 fixed, in the currency channel: a value formatted eagerly inside a memo has to be a dependency of that memo. #4272's PR adds displayLocale to this very array for exactly that reason and deliberately did not touch tenantCurrency, which is a different channel and a different card.
Fix sketch
Add tenantCurrency to the dependency array. The red-first case is a gantt with a currency tooltip field whose LocalizationProvider currency arrives after the first render, asserting the tooltip picks up the late value.
Generated by Claude Code
Pre-existing, found while threading the display locale through the same
useMemofor #4272. Filed rather than fixed — out of that card's ruled surface.The gap
packages/plugin-gantt/src/ObjectGantt.tsx— thetasksmemo formats every tooltip value eagerly inside its callback, including currency:but
tenantCurrency(fromuseLocalization()) is not in the memo's dependency array, which lists only[data, ganttConfig, objectSchema].Why it can be observed
useLocalization()is fed byGET /api/v1/auth/me/localization, which is cosmetic and non-blocking — it resolves after first paint. So the firsttaskscomputation runs withtenantCurrencystill undefined, and when the tenant's real currency arrives none ofdata/ganttConfig/objectSchemahas changed, the memo does not recompute, and the tooltip keeps the fallback currency until something unrelated invalidates it.This is the same class of bug as the locale one #4272 fixed, in the currency channel: a value formatted eagerly inside a memo has to be a dependency of that memo. #4272's PR adds
displayLocaleto this very array for exactly that reason and deliberately did not touchtenantCurrency, which is a different channel and a different card.Fix sketch
Add
tenantCurrencyto the dependency array. The red-first case is a gantt with acurrencytooltip field whoseLocalizationProvidercurrency arrives after the first render, asserting the tooltip picks up the late value.Generated by Claude Code