SCORM course not loading? How to find out why
Updated
When a SCORM course will not load, the cause is usually one of seven things, and each leaves its own sign: an upload error, a blank or 404 frame, an "API not found" message, missing pieces on an HTTPS site, a failure only in Safari, a Launch button that does nothing, or a course that plays but records nothing. Start from what you see, confirm the cause with one check, then fix the package or the LMS setting.
A SCORM course has two halves that can fail. The package is the zip the LMS unpacks: imsmanifest.xml says which file to open, and the files have to be where it says. The session is what happens in the learner's browser: the LMS opens the course in a frame or a new window, and the course finds the LMS's JavaScript API and reports progress through it. The first two causes below are package problems; the other five happen at run time.
Start from what you see
- The LMS rejects the upload, or says it cannot find a manifest: start with 2, then 1.
- The course frame is blank, shows a 404, or shows the LMS's "file not found" page: see 1.
- A message such as "Unable to acquire LMS API" or "API not found": see 3.
- Fonts, styling, a video or an embedded page is missing, or the course stops partway through loading: see 4.
- It works in Chrome but not in Safari, or Safari forgets the learner's progress: see 5.
- Clicking Launch does nothing, or the LMS says the course is open and no window appears: see 6.
- The course looks fine, but the LMS shows no completion, no score or no bookmark: see 7, then 3.
1. The launch file in imsmanifest.xml is wrong or missing
The LMS finds the file to open by following a chain in the manifest: the default <organization>, its first <item> with an identifierref, the <resource> with that identifier, and that resource's href. The href is a path relative to the manifest, plus any xml:base on the resource. If a link in the chain is broken, the LMS opens nothing, or opens a path that does not exist and the frame shows a 404.
To confirm it, open imsmanifest.xml in a text editor, follow the chain by hand, and check that the file named in href exists in the zip at exactly that path. The usual breaks:
- The file was renamed or moved after publishing, so href still points at the old name.
- The case differs: href says Index.html and the file is index.html. Windows servers ignore the difference; Linux servers do not, and return a 404.
- The item's identifierref matches no resource identifier, or the resource has no href at all.
- The path contains spaces or accented characters, which some LMSs encode wrongly.
In SCORM Flight Recorder, the Decode tab's flight plan shows the launch file it resolved from that chain, and the pre-flight checks name the broken link: "Launch file is not in the zip", an item that points at a resource that does not exist, "Launch resource has no href", and warnings for a case mismatch, spaces or non-ASCII characters in the path. If you launch anyway, the sandbox frame shows "Sandbox: file not found" with the path it looked for. The sandbox forgives case differences so you can keep testing, so treat a case warning as real even when the course plays.
Fix it in the authoring tool and republish rather than editing the manifest by hand. If you do edit it, make href match the file name exactly, character for character, and zip the package again.
2. The files are not at the root of the zip
imsmanifest.xml has to sit at the top level of the zip. If you zip the course folder instead of its contents, everything moves one level down into a folder, and the LMS looks for the manifest in the wrong place. Most LMSs then reject the upload with a message like "manifest not found" or "invalid package".
To confirm it, open the zip without extracting it. The first thing you see should be imsmanifest.xml next to the course files, not a single folder. On a Mac, right-clicking the course folder and choosing Compress produces exactly this nested layout. While you are there, check that the file is named imsmanifest.xml, all lower case.
Flight Recorder reports "Manifest not at root: most LMSs will reject this" and names the folder it found the manifest in, and the Files tab shows that folder as the package root. It still reads the package from that folder, so you can test the course while you fix the zip. It also flags a manifest file name in the wrong case.
To fix it, open the course folder, select everything inside it, and zip that selection. Republishing from the authoring tool also gives you a correctly built zip.
3. The course cannot find the SCORM API
The LMS gives the course a JavaScript object to talk to: API for SCORM 1.2, API_1484_11 for SCORM 2004. It puts that object in the window that holds the course, and the course goes looking for it, up through its parent frames and then in the window that opened it. If the search fails you see a message such as "Unable to acquire LMS API" or "API not found", or nothing at all: the course plays and tracks nothing.
The common reasons the search fails:
- Version mismatch. The course was published for SCORM 2004 and the LMS treats it as 1.2, or the other way round. The LMS only creates the object for the version it thinks it is running, so the course looks for a name that is not there.
- The course was opened outside the LMS: a copied link, a bookmark, or a file opened straight from a shared drive. There is no LMS window around it, so there is no API.
- The course is served from a different domain than the LMS page that holds the API. Browsers do not let a page read objects in a frame or window from another site, so the search fails even though the API exists. An LMS that serves content from another domain needs a cross-domain bridge to make this work.
- The course sits deeper in nested frames than its search goes. API search scripts usually stop after a fixed number of levels.
To confirm it, open the browser's developer console (F12 in Chrome, Edge and Firefox) and launch the course. In Chrome and Edge, a cross-domain block often shows as an error like "Blocked a frame with origin ... from accessing a cross-origin frame". To compare domains, find the course's <iframe> in the developer tools and check the domain in its src against the one in the address bar.
Flight Recorder installs both API objects on its own page and plays the course in a frame below them. If the course finds the API, the Log tab fills from the first call and the health check "LMSInitialize was called" ("Initialize was called" for SCORM 2004) turns green with the time it happened. If the course calls the object for the other SCORM version, a warning names the call and says a real LMS of the declared version would expose no such object. If the check stays grey with "Content has not initialised the session yet" after the course has loaded, the course is not looking for an API at all; check that LMS reporting is turned on in its publish settings.
If the course tracks in Flight Recorder and still cannot find the API in your LMS, the package's search works and the cause is on the LMS side: how it opens the window, or where it serves the files from.
To fix it, republish for the SCORM version the LMS is set to use, or re-upload the package as the right version. Make sure learners launch from the LMS, not from a saved link. For a cross-domain setup, ask the LMS admin or vendor how their platform handles content served from another domain.
4. Mixed content: http links on an https LMS
When the LMS page is served over HTTPS, browsers block insecure http:// scripts, stylesheets, fonts, frames and data requests inside it, and upgrade or block http:// images, audio and video depending on the browser. A course with hard-coded http:// links, to a web font, a video, an embedded web page, a script library on a CDN or a reporting endpoint, only partly loads. Fonts and styles are missing, a video or embedded frame stays blank, or a blocked script stops the course before it reaches the API.
To confirm it, open the developer console while the course loads and look for mixed content errors; each one names the URL that was blocked. Or search the unzipped course files for "http://". Ignore matches in imsmanifest.xml and the .xsd files, and the w3.org addresses in HTML and SVG files: those are namespace names, not downloads.
Flight Recorder does not scan the files for http:// links. Its sandbox is served over HTTPS, though, so a course with insecure links fails there the same way, and the console shows the same mixed content errors while the course plays in the Fly tab.
To fix it, change the links to https:// and republish, or put the file in the package and link to it with a relative path. If the server behind a link does not support HTTPS, the file has to move somewhere that does.
5. Safari and other browsers block cookies or storage across sites
If the course files come from a different site than the LMS page, the course frame is third-party content to the browser. That happens when the LMS serves content from a separate domain or CDN, when a vendor hosts the course and your LMS only holds a small package that loads it from their server, or when the course loads parts of itself from another site. Safari has blocked third-party cookies by default since 2020 and limits the storage that third-party frames can keep. Firefox keeps third-party cookies separate for each site by default, Chrome blocks them in Incognito windows, and users or IT policy can turn on blocking in any browser.
"Site" here means the registered domain: lms.example.com and content.example.com are the same site; example-lms.com and example-content.net are not.
What breaks depends on what the setup uses cookies for. A content server that needs a login cookie to hand out files shows a login page, an access-denied error or a blank frame inside the course. A cross-domain bridge that keeps its session in a cookie loses track of the learner. A course that saves its own progress in cookies or local storage forgets it.
To confirm it:
- Launch the same course with the same learner account in Chrome (a normal window, not Incognito) and in Safari. If it works only in Chrome, suspect this cause.
- Compare the domain in the address bar with the domain the course files come from: the src of the course's iframe, or the Network panel in the developer tools.
- As a test only, turn off Prevent cross-site tracking in Safari's Settings, under Privacy, and launch again. If the course now works, you have found the cause. Turn the setting back on.
Flight Recorder serves the package's files from the same site as its API, the way an LMS that hosts its own uploads does, so they are never third-party there. A course that runs and tracks in Flight Recorder but fails only in Safari on your LMS points at the hosting setup, not the package.
The fix is on the hosting side: upload the full package to the LMS instead of one that loads the course from elsewhere, or serve content from a subdomain of the LMS's own domain. If the setup has to stay cross-domain, ask the LMS or content vendor how they support Safari's default settings; some use the browser's Storage Access API, which asks the learner for permission. Asking learners to turn off tracking protection is not a fix.
6. A popup blocker stops a new-window launch
Many LMSs can open a course in a new window. Browsers let a page open a window straight from a click, and block a window that opens later: after a redirect, after a page loads, or after a delay. Some LMS launch pages open the course that way, and so do some courses, whose first page opens the real content in another window. The learner clicks Launch and nothing happens, or the LMS says the course is open and no window appears.
To confirm it, look for the blocked-window icon or message in the address bar right after you click Launch. Allow popups for the LMS's domain and launch again. If the course opens, the blocker was the cause.
Flight Recorder plays the course in a frame on its own page, so it does not test how your LMS opens the window. If the course itself opens a new window, the browser applies the same rules in Flight Recorder, so a blocked-window notice there means the course, not the LMS, is opening it.
To fix it, ask the LMS admin to allow popups for the LMS domain through browser policy, or set the course to open in the same window or an embedded frame if the LMS offers that. If the course opens its own window, turn off the authoring tool's option to launch the player in a new window and republish.
7. The course plays but never calls Initialize or Commit
Everything looks fine to the learner, but the LMS shows the course as not attempted or incomplete, records no score, or starts the learner from the beginning every time. The package and the browser are working; the course is not reporting through the API, or is reporting in the wrong order.
- It never calls Initialize (LMSInitialize in SCORM 1.2), so nothing it sends is recorded. This usually means LMS reporting is off, or set to the wrong standard, in the publish settings.
- It sets values before Initialize. The LMS ignores them.
- It never commits, and the session ends without Terminate (LMSFinish in 1.2). Many LMSs only save data on a commit or on terminate, and browsers do not always finish the work a page starts while its tab is closing, so the last values are lost.
- It reports a status the LMS does not count as complete, such as passed with completion still unknown in SCORM 2004.
Flight Recorder covers this case most directly. Launch the package in the Fly tab and play it as a learner. The cockpit runs seven health checks as the course talks to the API: whether it called LMSInitialize, set a completed or passed status, committed its score, called LMSFinish before unloading, kept suspend_data under the limit, reported session_time, and avoided API errors (with the SCORM 2004 names for 2004 packages). The warnings name order problems directly: "SetValue before Initialize" with a count, a critical warning when the content unloaded without LMSFinish or Terminate, and a note when the course terminated without ever committing. The cockpit counts commits as they happen, and Reload learner relaunches the course the way an LMS would, so you can check that it resumes.
To fix it, turn on LMS reporting in the authoring tool and publish for the version your LMS uses. If you control the code, call Initialize first, commit after each important change such as a score or a status, and call Terminate when the learner exits. The guide How to test a SCORM package without an LMS lists the other run-time mistakes worth checking before you upload.
When you hand it to the LMS admin
If the cause turns out to be on the LMS side, a specific ticket gets a faster answer. Include the browser and its version, whether the course opens in a new window or a frame, whether it fails for every learner or only in one browser, any errors from the developer console, and what the package does on its own. Flight Recorder's Log tab can copy or download a flight report with the health checks, warnings, the values the course wrote and the API log. Attach it, and the admin can rule the package in or out before looking at the platform.