imsmanifest.xml explained: the file that makes or breaks a SCORM import

imsmanifest.xml is the first file an LMS reads, and most import failures start there. Structure, the identifier chain, namespaces, and five fatal errors.

What the manifest declares

Every SCORM package is a zip, and every SCORM zip has a file called imsmanifest.xml at its root. Not in a subfolder, not renamed, not optional. It is the first file the LMS opens, and for most of the import it is the only file the LMS reads — the HTML, JavaScript and media are just bytes it copies. So when an import fails with a message as unhelpful as "invalid package", the fault is almost always in this one file.

The manifest answers four questions, in this order:

  • What specification is this? The <metadata> block declares the schema and version, and the root element declares namespaces. This is how the LMS decides whether to run 1.2 rules or 2004 rules.
  • What is the course structure? <organizations> holds a tree of <item> elements — the table of contents the learner sees.
  • What files exist, and which are launchable? <resources> lists every resource, its entry-point href, and whether it is a SCO (talks to the runtime) or an asset (does not).
  • How do those connect? Each item points at a resource by identifier. That pointer is where imports break most often.

Here is a complete, valid, minimal SCORM 1.2 manifest for a single-SCO course. Nothing has been elided — a package containing this file plus content/index.html will import.

<?xml version="1.0" encoding="UTF-8"?>
<manifest identifier="MANIFEST-fire-safety" version="1.0"
  xmlns="http://www.imsproject.org/xsd/imscp_rootv1p1p2"
  xmlns:adlcp="http://www.adlnet.org/xsd/adlcp_rootv1p2"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://www.imsproject.org/xsd/imscp_rootv1p1p2 imscp_rootv1p1p2.xsd
                      http://www.imsglobal.org/xsd/imsmd_rootv1p2p1 imsmd_rootv1p2p1.xsd
                      http://www.adlnet.org/xsd/adlcp_rootv1p2 adlcp_rootv1p2.xsd">

  <metadata>
    <schema>ADL SCORM</schema>
    <schemaversion>1.2</schemaversion>
  </metadata>

  <organizations default="ORG-default">
    <organization identifier="ORG-default">
      <title>Fire Safety</title>
      <item identifier="ITEM-1" identifierref="RES-1" isvisible="true">
        <title>Module 1: Evacuation</title>
        <adlcp:masteryscore>80</adlcp:masteryscore>
      </item>
    </organization>
  </organizations>

  <resources>
    <resource identifier="RES-1" type="webcontent"
              adlcp:scormtype="sco" href="content/index.html">
      <file href="content/index.html"/>
      <file href="content/scorm-api.js"/>
      <file href="content/styles.css"/>
    </resource>
  </resources>

</manifest>

Organizations and items

<organizations> is a container that can hold more than one <organization> — alternative structurings of the same content — and its default attribute names the one the LMS will use. In practice almost every package has exactly one organization, and the default attribute must still match its identifier exactly. A mismatch here produces an import that succeeds and then shows an empty course.

Inside an organization, <item> elements form the course tree. An item is either a leaf that points at a resource with identifierref, or a container with child items and no identifierref of its own. That is how you get a multi-module course:

<item identifier="ITEM-mod1">                     <!-- container: no identifierref -->
  <title>Module 1</title>
  <item identifier="ITEM-mod1-a" identifierref="RES-a">
    <title>Evacuation routes</title>
  </item>
  <item identifier="ITEM-mod1-b" identifierref="RES-b">
    <title>Assembly points</title>
  </item>
</item>

Two rules that catch people out. Every <item> must have a <title> — it is required, not decorative, and a missing title fails schema validation. And isvisible="false" hides an item from the learner's menu but does not exclude it from the course; strict LMSs still track it, which is a common source of "why is this course 90% complete forever".

In SCORM 1.2, <adlcp:masteryscore> on an item sets the pass mark. Be careful with it: when it is present, many platforms recompute cmi.core.lesson_status from the score the course reported and overwrite the status the course set. If your course decides pass/fail itself, leaving mastery score out is usually safer. SCORM 2004 replaces it with <imsss:minNormalizedMeasure> inside the sequencing block, which is a different mechanism with different rules — see the sequencing reference.

Resources and hrefs

The <resources> section is a flat list. Each <resource> carries four things that matter:

AttributeValueWhat it does
identifierAny unique stringThe target of an item's identifierref. Must be unique across the whole manifest.
typeAlways webcontentFixed by the specification for web content. Other values are not used in practice.
adlcp:scormtypesco or assetWhether this resource talks to the runtime API. A launchable, tracked unit is a sco; a PDF or image referenced by one is an asset. In 2004 the attribute is adlcp:scormType — note the capital T.
hrefRelative pathThe entry point the LMS launches. Required on a SCO. Relative to the manifest, or to xml:base if one is set.

Each <file href="…"/> child declares one file that belongs to the resource. Strict platforms verify that every declared file exists in the zip and refuse the import when one is missing; forgiving platforms ignore the list entirely and just serve whatever is there. This asymmetry is why a package can import cleanly on one LMS and fail on another with no change to the content — the second one is actually reading the manifest.

The identifier chain is the part worth internalising, because four of the five errors below are breaks in it:

<organizations default="ORG-1"> must equal <organization identifier="ORG-1"> <item identifierref="RES-1" must equal <resource identifier="RES-1" href= "content/index.html" must exist a real file in the zip Break any one link in this chain and the import fails, or the course opens empty.
Three string comparisons and one file lookup. Identifiers are case-sensitive and compared exactly.

The schema and version namespaces

An LMS decides which rule set to apply from the manifest, not from the file name or the content. Two places declare it, and they must agree.

SCORM 1.2SCORM 2004 (3rd/4th Ed.)
Default namespacehttp://www.imsproject.org/xsd/imscp_rootv1p1p2http://www.imsglobal.org/xsd/imscp_v1p1
ADL namespacexmlns:adlcp="http://www.adlnet.org/xsd/adlcp_rootv1p2"xmlns:adlcp="http://www.adlnet.org/xsd/adlcp_v1p3"
Extra namespaces—adlseq, adlnav, imsss for sequencing and navigation
<schemaversion>1.22004 3rd Edition or 2004 4th Edition
SCO type attributeadlcp:scormtype (lower case)adlcp:scormType (capital T)
Pass mark<adlcp:masteryscore>, 0–100<imsss:minNormalizedMeasure>, 0–1

The <schemaversion> string is matched literally by many platforms. 2004 4th Edition is accepted; 2004 4th ed., 4th Edition and 2004v4 are not. Getting this wrong is the classic cause of a 2004 package being imported as 1.2, which then silently discards cmi.success_status because 1.2 has no such element.

A 2004 manifest with 1.2 namespaces is the single most common version failure. It happens when someone edits an old manifest to "upgrade" a course rather than re-exporting it. The package imports — the XML is valid — and then the LMS applies 1.2 rules to content that expects 2004 ones. Symptom: completion works, pass/fail vanishes. Background in SCORM 1.2 vs 2004.

The five most common manifest errors

  1. The manifest is not at the zip root. Zipping the folder rather than its contents puts imsmanifest.xml one level down at course/imsmanifest.xml, where the LMS will not find it. On Windows, "Send to → Compressed folder" on a selected folder does exactly this. Fix: open the folder, select all the files inside, and zip the selection.
  2. identifierref does not match any resource identifier. Usually a typo or a case difference (RES-1 vs res-1); identifiers are case-sensitive. Symptom: the course imports and the item is unlaunchable, or the whole import is rejected on stricter platforms.
  3. The href points at a file that is not in the zip, or is spelled with different case than the actual file. Linux-hosted LMSs are case-sensitive where an author's Windows machine was not, so Content/Index.html works locally and 404s in production. Symptom: the course launches to a blank frame.
  4. A declared <file> is missing. Strict platforms verify every declared file. Symptom: import rejected with a message naming the file — one of the few genuinely useful SCORM error messages.
  5. organizations default does not match the organization's identifier, or <organizations> is empty. Symptom: an import that reports success and produces a course with no content.

Notice what these have in common: four of the five are string mismatches, not conceptual mistakes. They are exactly the class of error that a validator catches in a second and a human misses for an afternoon.

Validating before upload

You can check most of this yourself before an LMS ever sees the package. In order of effort:

  1. Confirm the location. unzip -l package.zip | head — imsmanifest.xml must appear with no directory prefix.
  2. Check it is well-formed XML. xmllint --noout imsmanifest.xml reports a line number for any syntax error. This catches unescaped ampersands in titles, which authoring tools produce more often than you would hope.
  3. Validate against the schema. If the .xsd files are in the package, xmllint --schema imscp_rootv1p1p2.xsd imsmanifest.xml --noout checks structure — required elements, attribute values, ordering.
  4. Resolve the identifier chain by hand or by script. Every identifierref must appear as a resource identifier; every SCO href and every declared <file> must exist in the zip at that exact path and case. A dozen lines of Python over zipfile and xml.etree does it, and is worth having in your build.
  5. Then test the runtime. A structurally perfect manifest tells you the package will import; it says nothing about whether the course reports completion. That is a separate question, covered in testing a package before upload, and when a package imports but misbehaves afterwards, work through the diagnostic checklist.

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