External Publication
Visit Post

Widget caching broken in both directions — static URI never refreshes, versioned URI throws "Failed to fetch template"

OpenAI Developer Community May 7, 2026
Source

We’re building a ChatGPT MCP app that returns a React/Vite widget via resources/read. We’ve hit a dead-end with the widget caching and need platform-level help.

Problem 1 — Static URI (current workaround): widget never updates

  • With a fixed URI (ui://widget/freelancers.html), ChatGPT caches the widget HTML on first load and re-fetches it non-deterministically — even after we deploy a completely new versioned-html.
  • We confirmed this in server logs: resources/read fires once, then subsequent tools/call requests come in with no resources/read at all. The Refresh button in the connector settings also fails silently.

Problem 2 — Versioned URI (recommended approach): “Error loading app — Failed to fetch template”

  • Following the official docs advice (“Treat the URI as your cache key. When you update the markup or bundle, version the URI.”), we changed our URI to include the build version: ui://widget/freelancers@gmd_backendv1-stage-{commit_hash}.
  • The tool call succeeds (25 freelancers returned in structuredContent), but the widget renders with “Error loading app — Failed to fetch template”.
  • Our server logs confirm resources/read was never called — ChatGPT appears to be looking for the old cached URI and failing when it doesn’t match the new one.

Our setup

  • MCP server: Node.js/Express, HTTP transport, protocol versions 2025-03-26 and 2026-01-26
  • Widget: React + Vite, served as text/html;profile=mcp-app via resources/read
  • URI in tools/list _meta: { ui: { resourceUri: WIDGET_URI } }
  • URI in resources/list, resources/templates/list, and resources/read response all use the same constant

Questions

  1. Why does versioning the URI cause “Failed to fetch template” even though the server correctly handles resources/read for the new URI?
  2. Is there an API or mechanism to explicitly invalidate the cached widget for a specific connector without the user having to delete and recreate the app?
  3. Is there a known-good pattern for deploying widget updates that neither breaks rendering nor serves stale HTML?

The refresh button throwing an error is also blocking us from manually clearing the cache during development. Any guidance appreciated.

Discussion in the ATmosphere

Loading comments...