{"record":{"id":"db52efc53baa5e02","repo":"TryGhost/Ghost","slug":"failed-to-fetch-site-data","errorCode":null,"errorMessage":"Failed to fetch site data","messagePattern":"Failed to fetch site data","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"apps/announcement-bar/src/utils/api.js","lineNumber":26,"sourceCode":"        };\n        return fetch(url, options);\n    }\n    const api = {};\n\n    api.announcementSettings = {\n        browse() {\n            const url = apiUrl;\n            return makeRequest({\n                url,\n                method: 'GET',\n                headers: {\n                    'Content-Type': 'application/json'\n                }\n            }).then(function (res) {\n                if (res.ok) {\n                    return res.json();\n                } else {\n                    throw new Error('Failed to fetch site data');\n                }\n            });\n        }\n    };\n\n    api.init = async () => {\n        let {announcement} = await api.announcementSettings.browse();\n        return announcement[0];\n    };\n\n    return api;\n}\n\nexport default setupGhostApi;\n","sourceCodeStart":8,"sourceCodeEnd":41,"githubUrl":"https://github.com/TryGhost/Ghost/blob/47d8b0e2ad2fd4757d3bc45f46c3ac165ff8a1fe/apps/announcement-bar/src/utils/api.js#L8-L41","documentation":"The announcement-bar app's setupGhostApi creates an announcementSettings.browse() method that GETs the configured apiUrl. If the response is not ok (HTTP non-2xx), it throws this generic message. The error propagates to api.init() which awaits browse() and is the app's bootstrap — a failure here prevents the announcement bar from loading.","triggerScenarios":"The GET to the announcement-bar apiUrl returns 4xx or 5xx. The apiUrl is misconfigured (points to a wrong endpoint, missing a content key, hits a non-existent route), the Ghost site is down, CORS blocks the cross-origin request (though CORS typically rejects before res.ok is evaluated), or the content API key is invalid yielding 401/403.","commonSituations":"The announcement-bar script is embedded on a page where the apiUrl variable was not set or points to a staging endpoint that is offline. The Content API key embedded in the script is expired or belongs to a different site. The Ghost version does not have the announcement-bar integration enabled.","solutions":["Verify the apiUrl and any API key passed to setupGhostApi are correct and point to the live Ghost Content API endpoint.","Check the Network tab for the failing request's status code and response body to distinguish 401 (bad key) from 404 (wrong URL) from 5xx (server).","Ensure the Ghost instance is running and the announcement-bar integration is installed and active.","Confirm CORS headers are present if the bar loads on a different origin than the API."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"function isAnnouncementApiConfigured(apiUrl: string | undefined): boolean {\n  return typeof apiUrl === 'string' && apiUrl.length > 0 && /^https?:\\/\\//.test(apiUrl);\n}","typeGuard":null,"tryCatchPattern":"try {\n  const announcement = await api.init();\n} catch (e) {\n  if (e instanceof Error && e.message === 'Failed to fetch site data') {\n    // render the bar in a disabled/hidden state; log the apiUrl\n  }\n  throw e;\n}","preventionTips":["Verify the apiUrl passed to setupGhostApi is the correct Content API endpoint before initializing.","Wrap api.init() in a try/catch at the app bootstrap so a failure does not crash the host page.","Check the Network tab for the failing request's status to distinguish config errors (401/404) from server errors (5xx)."],"tags":["network","fetch","announcement-bar","api","bootstrap"],"backgroundTag":null,"analyzedSha":"47d8b0e2ad2fd4757d3bc45f46c3ac165ff8a1fe","analyzedAt":"2026-08-13T01:25:26.651Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}