Studio document lists stuck on loading spinner after upgrade

11 replies
Last updated: Feb 3, 2022
I have a project that I haven’t touched for a bit. My client content editors let me know this morning that some of the content on the site was no longer showing up. I logged into the Studio, and while the desk structure is all there, I only get the loading spinner when I try to see any document lists.
I upgraded Studio from 2.21.9 to 2.26.0 and deployed, but that didn’t make a difference.

When I put a GROQ query into Vision, I can see the output. But something appears to be broken with the API document list query in Studio. I’ve tried multiple browsers with the same result. Any suggestions as to my next troubleshooting step?
AI Update

This is a frustrating issue! Since Vision works but the desk structure document lists are stuck on loading spinners, this points to a specific problem with the Studio's real-time listener connection rather than a general API access issue.

The most common cause of this exact symptom is a blocked EventSource connection. The Sanity Studio desk tool uses an EventSource connection to the /data/listen endpoint at api.sanity.io to provide real-time updates to document lists. When this connection fails but regular queries work (like in Vision), you get infinite loading spinners.

Here are your next troubleshooting steps:

1. Check Browser Console for EventSource Errors

Open your browser's developer console when the Studio is loading. Look for errors like:

  • "Could not establish EventSource connection"
  • Failed requests to URLs containing /data/listen
  • CORS errors or network errors related to api.sanity.io

2. Network/Firewall Issues

The EventSource connection can be blocked by:

  • Corporate firewalls or VPN settings
  • Browser extensions (especially ad blockers or privacy tools)
  • Network security policies that block Server-Sent Events (SSE)

Ask your content editors to try:

  • Disabling browser extensions temporarily
  • Trying from a different network (mobile hotspot, home network)
  • Checking if their IT department blocks EventSource/SSE connections

3. Check CORS Configuration

If you have custom CORS settings in your Sanity project, verify they're not blocking the listen endpoint. Go to manage.sanity.io, select your project, and check the CORS origins under Settings → API.

4. Clear Studio Cache

Sometimes Studio caching can cause issues after upgrades:

  • Clear browser cache completely
  • Delete node_modules and reinstall
  • Run sanity build to rebuild the Studio assets

5. Check for Custom Structure Issues

If you have custom structure configuration in sanity.config.ts, temporarily comment it out to see if a custom structure function is causing issues. Even if the structure appears fine, there could be an issue with how document lists are being queried.

6. Verify Dataset Access

Ensure the dataset permissions haven't changed. In manage.sanity.io, check that the dataset is accessible and that authentication tokens (if any) are still valid.

Since you upgraded from 2.21.9 to 2.26.0, there might also be compatibility issues with your configuration. If none of the above works, consider testing with a fresh Studio v3 setup (Studio v2 is deprecated), as this would resolve many legacy issues and give you access to current support.

The fact that Vision works confirms your API credentials and dataset access are fine—this is purely about the real-time listener connection that powers the desk tool's document lists.

Show original thread
11 replies
Thanks for the quick reply, Rachael! I am using a custom desk structure.
It sounds like something's not resolving, then. I think there were some changes to Structure Builder in between those versions if I recall correctly. Do you mind sharing your structure?
Sure! Here it is:

import S from '@sanity/desk-tool/structure-builder'
import { FaFirstAid, FaWpforms, FaMapMarkedAlt } from "react-icons/fa";
import { RiMapPinUserFill, RiGlobeFill } from "react-icons/ri";
import { MdLocationCity, MdLibraryBooks } from "react-icons/md";
import { GoSettings } from "react-icons/go";

const hiddenDocTypes = listItem =>
  !['category','siteSettings','story'].includes(listItem.getId())

export default () =>
  S.list()
    .title('Content')
    .items([
      S.listItem()
        .title('Stories')
        .icon(RiMapPinUserFill)
        .schemaType('story')
        .child(S.documentTypeList('story').title('Stories')),
      S.listItem()
        .title('Places')
        .icon(RiGlobeFill)
        .child(
          S.list()
          .title('Place Types')
          .items([
            S.listItem()
            .title('Cities')
            .icon(MdLocationCity)
            .schemaType('city')
            .child(
              S.documentTypeList('city').title('Cities')
            ),
            S.listItem()
            .title('Refugee Camps')
            .icon(FaFirstAid)
            .schemaType('camp')
            .child(
              S.documentTypeList('camp').title('Refugee Camps')
            )
          ])
        ),

      S.listItem()
      .title('Pages')
      .icon(MdLibraryBooks)
      .child(
        S.list()
        .title('Individual Pages')
        .items([
          S.listItem()
            .title('Explore the Map')
            .icon(FaMapMarkedAlt)
            .child(
              S.editor()
                .id('explore')
                .schemaType('explore')
                .documentId('explore')
            ),
          S.listItem()
            .title('Share Your Story')
            .icon(FaWpforms)
            .child(
              S.editor()
                .id('share')
                .schemaType('share')
                .documentId('share')
            )
          ])
        ),

      S.listItem()
        .title('Settings')
        .icon(GoSettings)
        .child(
          S.editor()
            .id('siteSettings')
            .schemaType('siteSettings')
            .documentId('siteSettings')
        ),
    ])
Sure! Here it is:

import S from '@sanity/desk-tool/structure-builder'
import { FaFirstAid, FaWpforms, FaMapMarkedAlt } from "react-icons/fa";
import { RiMapPinUserFill, RiGlobeFill } from "react-icons/ri";
import { MdLocationCity, MdLibraryBooks } from "react-icons/md";
import { GoSettings } from "react-icons/go";

const hiddenDocTypes = listItem =>
  !['category','siteSettings','story'].includes(listItem.getId())

export default () =>
  S.list()
    .title('Content')
    .items([
      S.listItem()
        .title('Stories')
        .icon(RiMapPinUserFill)
        .schemaType('story')
        .child(S.documentTypeList('story').title('Stories')),
      S.listItem()
        .title('Places')
        .icon(RiGlobeFill)
        .child(
          S.list()
          .title('Place Types')
          .items([
            S.listItem()
            .title('Cities')
            .icon(MdLocationCity)
            .schemaType('city')
            .child(
              S.documentTypeList('city').title('Cities')
            ),
            S.listItem()
            .title('Refugee Camps')
            .icon(FaFirstAid)
            .schemaType('camp')
            .child(
              S.documentTypeList('camp').title('Refugee Camps')
            )
          ])
        ),

      S.listItem()
      .title('Pages')
      .icon(MdLibraryBooks)
      .child(
        S.list()
        .title('Individual Pages')
        .items([
          S.listItem()
            .title('Explore the Map')
            .icon(FaMapMarkedAlt)
            .child(
              S.editor()
                .id('explore')
                .schemaType('explore')
                .documentId('explore')
            ),
          S.listItem()
            .title('Share Your Story')
            .icon(FaWpforms)
            .child(
              S.editor()
                .id('share')
                .schemaType('share')
                .documentId('share')
            )
          ])
        ),

      S.listItem()
        .title('Settings')
        .icon(GoSettings)
        .child(
          S.editor()
            .id('siteSettings')
            .schemaType('siteSettings')
            .documentId('siteSettings')
        ),
    ])
Nothing is immediately jumping out to me with this. I'll have to play around with it. Could you share your Studio's schema folder with me? Feel free to DM it if you don't want it to be public.
I’ll be happy to do that. I’ll DM you.
I’ve been poking around a bit more on slack and in my files. Is
@sanity/components
version 2.14.0 the most recent? That’s what’s in my package.json.
Ok I was able to get your schemas/desk up and running. Looks like there's nothing inherently wrong with your structure. Are there any errors or anything in your console when you get this infinite spinning?
Oh and can you export a copy of your dataset and DM it to me?
I did eventually see a console error. I’ll DM it to you. Oh, and my spinner eventually gave me an error in the studio: Error: 
Could not establish EventSource connection
Ok, everything looks like it's working for me in both local and deployed Studios with your data. Just to see if it kickstarts everything, can you try undeploying the Studio, deleting your node modules with
rm -rf node_modules
, reinstalling with
npm i
, then redeploying?
Hmmm… sadly, that didn’t seem to make a difference. I’ve also cleared all my browsing data and turned off all extensions in Chrome.

Sanity – Build the way you think, not the way your CMS thinks

Sanity is the developer-first content operating system that gives you complete control. Schema-as-code, GROQ queries, and real-time APIs mean no more workarounds or waiting for deployments. Free to start, scale as you grow.

Was this answer helpful?