Skip to main content
Question

Plugin can't detect mid-session library subscription changes

  • May 5, 2026
  • 0 replies
  • 12 views

Ben_Ho

Problem

There's no plugin or REST API that exposes a fresh view of which team libraries are currently subscribed to by a file. After a user adds or removes a library mid-session, every documented and undocumented signal continues to report the prior subscription state until the file is reloaded. This makes it impossible to write a plugin that correctly classifies orphan references (variables, styles, components from libraries no longer linked to the file) without asking the user to reload Figma after every library change.

Use case

We're building a plugin that scans a file for orphan references — bindings to variables/styles/components from libraries that aren't subscribed by the file. The orphan signal we use today is figma.teamLibrary.getAvailableLibraryVariableCollectionsAsync(): any remote collection whose key isn't in that response is classified as orphan. This works perfectly when the cache is fresh (after file load), but breaks silently when the user changes subscriptions mid-session.

This pattern is widely useful — every plugin that helps with library migrations, design system audits, or orphan cleanup hits this exact wall.

Reproduction

  1. Open file F. Confirm via figma.teamLibrary.getAvailableLibraryVariableCollectionsAsync() that library L is in the result.
  2. Without reloading the file, remove library L from F's library subscriptions in the Libraries panel.
  3. Re-call getAvailableLibraryVariableCollectionsAsync(). L is still in the response.
  4. Reload the file. Re-call. L is now correctly absent.

The same staleness applies in the install direction — getAvailableLibraryVariableCollectionsAsync() does not include a freshly-added library until reload.

Workarounds we attempted (all dead ends)

  • figma.variables.importVariableByKeyAsync(varKey) as a probe. Succeeds for any variable already locally cached, even after the source library is unlinked → false negative on uninstall.
  • figma.teamLibrary.getVariablesInLibraryCollectionAsync(key) as a probe. Returned 993 stale entries for a library that had been removed → false positive.
  • figma.variables.getSubscribedVariables()` (undocumented). Returns sparse data (4 of 6,320 bound variable refs in our test file). Inconsistent with documented behavior; can't be relied on.
  • REST GET /v1/files/:file_key/variables/local. Returns the same usage data with the same staleness — stale-bound collections (e.g. Anvil2 Theme long after removal) appear in meta.variableCollections exactly as they do in the plugin sandbox. Verified empirically: re-subscribing a library mid-session does not propagate to the REST response within 30+ seconds — full file reload is still required.
  • REST GET /v1/files/:file_key. No library-subscription field on the response.
  • REST library-analytics endpoints(/v1/analytics/libraries/:library_file_key/variable/usages, etc.). Wrong direction (library → consumers, not consumer → libraries), wrong cadence (analytics-paced, not real-time), and we don't have library file keys from the variable metadata.
  • Webhook eventsLIBRARY_PUBLISH exists but fires on library publishes, not on consumer-side subscription changes. No LIBRARY_SUBSCRIBE/LIBRARY_UNSUBSCRIBE event.
  • Plugin events (figma.on). figma.on rejects every library-related event name we tried. Only documentchange and stylechange are valid; neither fires on subscription changes.
  • Side-effect probes. Calling importVariableByKeyAsync or getVariablesInLibraryCollectionAsync with nonexistent keys does not refresh the cache — back-to-back calls return identical results.
  • Variable / Collection methods. getPublishStatusAsync is local-only ("can only query publish status for local variables"). resolveForConsumer succeeds identically for live-linked and stale-bound variables. hiddenFromPublishing, isExtension, scopes, etc. — none differ between live and stale links.

There is no field on a Collection or Variable, in either the plugin or REST APIs, that distinguishes "currently subscribed by this file" from "previously subscribed, now orphaned, locally cached." Cached resolution paths work identically for both states.

Concrete API requests (any one of these would unblock us)

In rough order of preference:

  1. figma.teamLibrary.refreshAsync() — explicitly refresh the file's subscription cache, returning a Promise that resolves when the next getAvailableLibraryVariableCollectionsAsync() will return fresh data.
  2. librarysubscriptionchange event on figma.on(...) — fires when the user subscribes/unsubscribes a library on this file. Carries the changed library's libraryName and the resulting available collections.
  3. A field on LibraryVariableCollection / BaseStyle / ComponentNode — e.g. isLinked: boolean, indicating whether the source library is currently linked to this file (distinct from "exists in team libraries").
  4. REST endpoint GET /v1/files/:file_key/libraries — returns the libraries currently subscribed by the file. Server-side, no plugin sandbox cache involvement.
  5. A documented contract for getSubscribedVariables — clarify what "subscribed" means there and ensure it returns a complete list. Today its semantics are unclear and the data is sparse.

Forum thread referenced

Why are library variables cached when using getAvailableLibraryVariableCollectionsAsync? — closed with 0 replies; the issue is real and unaddressed.

Impact

The current behavior makes it impossible to ship a fully-automated orphan-detection plugin. The best we can do is show a banner advising users to reload Figma after subscription changes, which puts the burden on users to remember a non-obvious workflow constraint. Any of the API additions above would let plugins detect and react to subscription changes correctly, eliminating an entire class of false-positive and false-negative orphan classifications.