SCORM package not working in your LMS? A diagnostic checklist

A SCORM package fails at one of four gates: import, launch, tracking, or persistence. Find your symptom, get the cause and the fix, without guessing.

Start with the symptom

"The SCORM package is not working in the LMS" describes four completely different failures with four unrelated causes, and the fastest way to waste a day is to start rebuilding the course before knowing which one you have. A SCORM package passes through four gates, in order. It gets imported, it gets launched, it talks to the runtime, and what it says gets persisted. Each gate fails in its own way and produces its own symptom.

1 · Import LMS reads the manifest 2 · Launch SCO opens, finds API 3 · Track SetValue calls succeed 4 · Persist Commit lands, survives "invalid package" Manifest: path, identifiers, namespaces blank frame Bad href, case, mixed content, popup always "not attempted" API not found, Initialize failed completion disappears No commit, suspend truncation, exit value The test that splits them: open the console on the launched course and read the runtime call log. No calls = gate 2 or 3. Calls returning "false" = gate 3. All "true" yet nothing saved = gate 4.
Work left to right. Do not investigate a later gate until the earlier ones pass.

Everything below is organised by gate. Find your symptom, apply the checks in order, and stop when one of them reproduces the problem.

It will not import

The LMS rejects the zip, or accepts it and shows an empty course. The content is irrelevant here — the LMS has not looked at it. This is a manifest problem in almost every case.

What you seeLikely causeFix
"Invalid package", "manifest not found"imsmanifest.xml is not at the zip root — the folder was zipped instead of its contentsunzip -l package.zip | head. If the manifest shows a directory prefix, re-zip from inside the folder.
Import fails naming a fileA <file href> declared in the manifest is missing from the zipAdd the file or remove the declaration. Strict platforms verify all of them.
XML parse error, often with a line numberMalformed manifest — usually an unescaped & in a titlexmllint --noout imsmanifest.xml, then escape as &amp;.
Imports, but the course is emptyorganizations default does not match the organization identifier, or the organization has no itemsCompare the two strings exactly; they are case-sensitive.
Imports, but an item will not openidentifierref matches no resource identifierResolve the chain — every reference must hit a real resource.
Rejected as the wrong version<schemaversion> text or namespaces do not match the declared editionUse the exact strings: 1.2, 2004 3rd Edition, 2004 4th Edition.
Upload fails before import startsThe zip exceeds the platform's upload limitA server limit, not a SCORM one. Compress media or host large video externally.

All of these are checkable in about two minutes without an LMS. The full anatomy, including the identifier chain and the namespace table, is in imsmanifest.xml explained.

It imports but will not launch

The course appears in the catalogue, the learner clicks it, and gets a blank frame, a broken-image page, or a window that opens and closes. The LMS did its job; the content is not loading.

  • Check the href resolves. Open the browser's network tab and launch. A 404 on the entry point means the resource href does not match the real file path. The usual culprit is letter case: authoring on Windows (case-insensitive) and hosting on Linux (case-sensitive) lets Content/Index.html work locally and fail in production.
  • Check for mixed content. If the LMS is HTTPS and the course loads a script, font or video over HTTP, the browser blocks it silently and the page renders blank or unstyled. The console shows "Mixed Content" warnings. Fix the URLs to HTTPS or bundle the asset.
  • Check the launch mode. A course built to run in a new window can break inside an iframe, and vice versa, because the API-finding code walks a window hierarchy that is not the one it expected. If the LMS offers a choice — new window vs. current — try the other.
  • Check the popup blocker. Platforms that launch in a new window lose to popup blockers, which is invisible to the learner apart from "nothing happened".
  • Check for a missing entry file. A resource with no href, or an href pointing at a folder rather than a file, produces a directory listing or a blank page.

It launches but tracks nothing

The course runs perfectly for the learner. The LMS shows "not attempted" or "in progress" forever, with no score. This is the most common report, and it has one dominant cause: the content never established a session with the runtime.

Confirm it in the console before doing anything else. On the launched course, in the frame the course runs in:

// SCORM 1.2 — walk up looking for the API object
var w = window, api = null, n = 0;
while (w && n++ < 10) { if (w.API) { api = w.API; break; } w = w.parent === w ? null : w.parent; }
console.log('1.2 API found:', !!api);

// SCORM 2004 uses a different object name
var w2 = window, api2 = null, m = 0;
while (w2 && m++ < 10) { if (w2.API_1484_11) { api2 = w2.API_1484_11; break; } w2 = w2.parent === w2 ? null : w2.parent; }
console.log('2004 API found:', !!api2);

Both false means the content cannot see the API and nothing it does will ever be recorded. Causes, in order of frequency:

  • Version mismatch. The course looks for API (1.2) and the LMS launched it as 2004, providing API_1484_11 — or the reverse. This is the same root cause as the namespace error in the manifest section: the package says one version and the content speaks another.
  • The API is above an opener, not a parent. Content launched in a new window must also search window.opener and its parents. Older hand-written API-finder code often does not.
  • Cross-origin frames. If the LMS serves the course from a different domain than the player, the walk up the window hierarchy throws a security error and stops. The console shows a cross-origin message. This is a hosting configuration problem, not a package problem.
  • A race at launch. The content calls LMSInitialize before the LMS has finished placing the API object. Symptom: error 301 (not initialized) on subsequent calls in 1.2, or 102/122 in 2004.

If the API is found and calls still fail, read the error code rather than guessing — LMSGetLastError() after a failed call, or GetLastError() in 2004. Code 401 means the element is not implemented on that platform; 403/404 mean you are writing a read-only or reading a write-only element; 405 means the value is the wrong type or outside its vocabulary. The full code list for each version is in the 1.2 data model reference and the 2004 data model reference.

It tracks but loses completion

The hardest one, because it works in testing and fails for some learners. The runtime log looks healthy, values are set, and the record is still wrong afterwards. Four mechanisms account for nearly all of it:

  1. The final commit never landed. The course sets completion and relies on the browser's unload event to commit. Unload handlers are unreliable, fire late, and are cancelled outright on mobile. Test by closing the tab abruptly right after finishing — not with the course's own exit button — then reopening.
  2. Suspend data was truncated. The state string exceeded the cap (4,096 characters in 1.2; 4,000 in 2004 2nd Edition; 64,000 in 3rd/4th), the platform truncated it silently, and on resume the course could not parse its own state and started over. Details and how to measure it: suspend data limits.
  3. Status was written before score. Some platforms lock the attempt record the moment they first see completed or passed and ignore later writes — recording a completion with an empty score. Write score first, then success, then completion, then commit.
  4. The exit value ended the attempt. The course must set cmi.core.exit to suspend (1.2) or cmi.exit to suspend (2004) before finishing if the attempt is meant to resume. Left empty, several platforms close the attempt and discard the suspend data, even though it was written correctly and was under the cap.

A fifth, less common: a masteryscore in the manifest causing the LMS to recompute pass/fail from the score and overwrite the status the course set. The full catalogue with the fix for each is in why SCORM completions go missing.

When the package is fine and the platform is not

If the package imports, launches, tracks, and persists on one LMS and fails on another, the package is probably not the problem. The specification leaves several behaviours open, and platforms take different positions on them: how often Commit may be called before it is throttled, whether an over-long suspend string is truncated or rejected, what a status set before a score records, whether interactions are stored or accepted and dropped, and what an empty exit value does to an attempt.

Two practical consequences. First, "it worked in our LMS" is weak evidence about a customer's LMS — the differences that matter are exactly the ones no conformance badge covers. Second, when you are shipping to platforms you do not control, the useful test is not "does it conform" but "does it survive the behaviours the destination actually has". That is what replaying a finished session against saved runtime behaviour profiles is for, and it is the difference between passing a spec check and passing a customer's environment. If you are also moving content between platforms, keeping SCORM working through an LMS migration covers what breaks in transit.

Where SCORM Central fits

SCORM Central converts files you already have — PDF, PowerPoint, Word, or video — into SCORM 1.2, SCORM 2004, cmi5, or xAPI, and shows you the manifest, the full runtime log, and a conformance report on every build, so you can see exactly what a package will report before an LMS does. See how it works, or start with one file.

Keep reading

How-to4 min Convert SCORM to video or PowerPoint? Can you convert SCORM to MP4, HTML5 or PowerPoint? What is inside a package, the routes that work, and what you lose with each one. Explainer4 min Does your LMS support xAPI? How to check What xAPI LMS support really means: launching xAPI content, a built-in LRS, or forwarding statements. The questions to ask and a quick test to run. Explainer4 min LMS vs LRS: what is the difference? LMS vs LRS: an LMS runs courses and learners; an LRS stores xAPI statements. Why SCORM does not use an LRS, and when you need both.

Start with one file.

Upload something you already have and see it as a package, with the conformance report and the runtime log alongside it.

→ upload  handbook.pdf
  parsing … 14 sections
  building manifest …
  validating … 0 errors, 2 notes
✓ handbook-scorm2004.zip
✓ preview link ready