A SCORM course shows incomplete when the package never sends a status the LMS accepts as completion. In Moodle, only two values count — passed and completed — so a package reporting failed, browsed or incomplete can never satisfy activity completion. The most common causes are a mismatch between the authoring tool's "Report status to LMS as" setting and Moodle's "Require status" checkboxes, a mastery score silently rewriting the status to failed, and the learner closing the browser tab before LMSFinish() runs.
Key takeaways
- Moodle accepts only passed and completed as completion. Failed, incomplete, browsed and not attempted will never mark an activity complete, no matter how you configure it.
- If your package reports Passed/Incomplete but you only ticked Completed under Require status, the course can never complete. This single mismatch causes more stuck-incomplete tickets than anything else.
- An adlcp:masteryscore element in the manifest lets Moodle overwrite whatever status the course reported — scoring below it rewrites completed to failed at exit.
- Chrome removed synchronous requests during page unload in v80, so a learner who closes the tab can lose the final LMSFinish() entirely. Moodle's Auto-commit setting is the defence.
- Activity completion updates instantly; course completion waits for cron. A green activity tick with a course still "In progress" is usually a cron problem, not a SCORM problem.
- Check your Moodle version — false SCORM completions (MDL-78518) were only fixed in 4.4.9, 4.5.5 and 5.0.1, and the 4.3 database refactor broke completion outright until 4.3.1.
It is one of the most frustrating tickets in corporate L&D. A learner watches every slide, passes the quiz, closes the window — and the LMS still shows the course as incomplete. The learner is annoyed, the compliance report is wrong, and nobody can tell whether the problem is the course, the LMS, or the learner.
Almost every guide on this topic tells you to "check your completion settings." That is not an answer. The real reason is that SCORM completion is a chain of about six handoffs — authoring tool → package manifest → the LMS runtime API → the database → the activity completion rule → the course completion cron — and a break anywhere in that chain looks identical from the outside.
We run Moodle for a living and build SCORM content, so we see both ends of this problem. Below are the nine causes we actually encounter, in rough order of how often they turn out to be the culprit, each with the specific setting, spec behaviour or code path behind it — and the fix.
First, understand what "complete" actually means
SCORM does not have one completion flag. It has several, and which ones exist depends on the version you published in.
SCORM 1.2 has a single element, cmi.core.lesson_status, with exactly six legal values:
passedcompletedfailedincompletebrowsednot attempted
Notice the design flaw: one element is carrying two completely different ideas — did you finish it and did you pass it. There is no way to say "completed but failed" in SCORM 1.2, because that needs two values and you only get one.
SCORM 2004 fixed this by splitting them into two elements:
| Element | Legal values | Answers the question |
|---|---|---|
cmi.completion_status | completed, incomplete, not attempted, unknown | Did the learner get through it? |
cmi.success_status | passed, failed, unknown | Did the learner master it? |
And here is the part that catches almost everyone. Moodle accepts only two values as completion — passed and completed. That is not a configuration choice; it is hardcoded. In mod/scorm/lib.php, the function that builds the "Require status" checkboxes returns exactly two options. So failed, incomplete, browsed and not attempted can never satisfy activity completion in Moodle, however you configure the activity.
Hold on to that. Six of the nine causes below are really variations on "the package sent something that isn't passed or completed."
The 9 causes, and how to fix each one
1. The package never reported a completion status at all
Before blaming the LMS, confirm the course is actually saying anything. In Articulate Storyline 360 the tracking options are Slides viewed, Quizzes and Completion triggers — and Articulate's own documentation is explicit that whichever option the learner satisfies first is the one reported, and only one status is ever sent, even if they meet several criteria.
The classic failure: a course is set to track by "number of slides viewed", the learner skips two optional slides, and the threshold is never met. The course looks finished to a human and is genuinely unfinished to the LMS.
Fix: pick one unambiguous completion trigger and make it reachable. For compliance content we almost always use an explicit completion trigger on a final "Finish" button rather than slide counts — it removes the ambiguity entirely and it is far easier to defend in an audit.
2. "Passed" is not "Completed" — the single most common mismatch
This is the one. In Storyline and Rise, the Report status to LMS as setting offers four options:
| Option | What it actually sends |
|---|---|
| Passed/Incomplete | Completion and success status |
| Passed/Failed | Completion and success status |
| Completed/Incomplete | Completion status only |
| Completed/Failed | Completion status only |
Articulate recommends Passed/Incomplete or Passed/Failed, because they report both statuses. Sensible advice — but in SCORM 1.2 that means the package sends lesson_status = passed, and Moodle treats passed and completed as two distinct, separate flags.
So if your Moodle activity has Require status with only Completed ticked, and your package reports Passed, the activity will never complete. Both sides are configured "correctly" and nothing works.
Fix: in the SCORM activity's completion settings, tick both Passed and Completed under Require status. Moodle treats multiple ticks as "any one of these will do." Worth noting: on Moodle 3.11 specifically, ticking both was actually broken — a bitwise comparison required both flags simultaneously, which is impossible since a single attempt only ever holds one value. That was MDL-71796, fixed in 3.11.1.
3. A mastery score silently rewrote your status to "failed"
If your imsmanifest.xml contains an <adlcp:masteryscore> element, the LMS is entitled to override whatever status the course reported. Moodle does exactly that, and the behaviour is on by default — the site setting is called "Mastery score overrides status".
Moodle's own help text does not hide it: when LMSFinish is called and a raw score exists, the status is recalculated from the raw score and the mastery score, and any status provided by the SCORM package — including "incomplete" — will be overridden.
The consequence: a learner who genuinely finished every slide but scored 68% against a mastery score of 70 gets their completed silently rewritten to failed. And failed, as established, can never satisfy Moodle completion.
Two related traps in the same code path:
- Preview mode never counts. If the learner enters through Preview, Moodle sets the mode to
browseand the credit tono-credit. The result is a status ofbrowsed— not an accepted completion value. Testing in preview and concluding "it works" is a very common false positive. - A silent free pass. If a SCO initialises and exits without ever setting a status, Moodle rewrites
not attemptedtocompletedatLMSFinish. Some courses complete for the wrong reason.
Fix: decide deliberately whether you want score-based gating. If you do, make the mastery score match the pass mark shown to the learner. If you do not, remove <adlcp:masteryscore> from the manifest and let the course own its own status.
4. The learner closed the tab and LMSFinish() never ran
SCORM courses are supposed to commit their final state on exit. Most authoring tools attach that to the browser's unload event — which worked fine until Chrome 80 removed the ability for scripts to make synchronous requests during beforeunload and unload.
Rustici, who maintain the SCORM standards, document the consequence bluntly: the registration may stay in an incomplete status even though the user completed the course, exit times come back null, and completion, success, score and duration never roll up.
This is why the symptom is so maddeningly intermittent. The learner who clicks your Exit button completes. The learner who hits ⌘W does not.
Fix, and it is a good one: turn on Moodle's Auto-commit setting (Site administration → Plugins → Activity modules → SCORM package). Moodle's description — "useful for SCORM objects which do not save their data regularly" — undersells it. It periodically writes tracking data to the database instead of relying on a final commit that may never arrive. For comparison, SCORM Cloud commits every 10 seconds by default. Pair it with a visible, unmissable Exit button in the course itself.
5. A multi-SCO package where only one SCO finished
Many packages contain several SCOs. Moodle has a setting for this, labelled "All SCOs must return completion status" in Moodle 4.3 and later (it was "Require all scos to return completion status" in 4.2 and earlier — worth knowing if you are reading an older tutorial).
When that box is ticked, every SCO in the package must return passed or completed. Items marked as asset rather than sco in the manifest are excluded from the check — which is precisely why some multi-SCO packages complete and structurally similar ones do not.
Fix: unzip the package and count the <item> entries with adlcp:scormtype="sco". Then open the Basic report in Moodle and confirm each of those SCOs has a qualifying status. If your package has one real module plus three reference PDFs that were authored as SCOs, either untick the setting or republish those references as assets.
6. suspend_data overflowed and progress quietly rolled back
SCORM stores bookmarking and resume state in cmi.suspend_data, and it has a hard size limit:
| Version | suspend_data limit |
|---|---|
| SCORM 1.2 | 4,096 characters |
| SCORM 2004 2nd Edition | 4,000 characters |
| SCORM 2004 3rd & 4th Edition | 64,000 characters |
The symptom is not an error message. Rustici describe it exactly as learners experience it: you exit a course only to resume at an earlier point. The learner reaches slide 40, exits, comes back and lands on slide 22 — because the oversized write was rejected and the LMS is still holding the last string that fit. They re-tread the same ground forever and the course never completes.
There is a Moodle-specific wrinkle here that trips up teams running more than one LMS. Moodle has a site setting called "SCORM standards mode" which is off by default — and when it is off, Moodle quietly allows 64,000 characters even for SCORM 1.2 packages. So a bloated package works perfectly on Moodle and fails the moment a client loads it into a spec-strict LMS.
Fix: republish to SCORM 2004 3rd or 4th Edition, or reduce what the course is storing — fewer slides per module, shorter slide and answer titles, and turn off resume if the course is short enough not to need it.
7. "Force new attempt: Always" is wiping progress on every visit
Moodle's Force new attempt setting has three options, and the third one causes real damage when it is chosen carelessly. Set to Always, Moodle's help text warns that every single re-entry generates a new attempt and the learner is not returned to where they left off.
For a 45-minute course that people take across two sittings, that means completion is mathematically impossible unless someone finishes in one go.
Two neighbouring settings compound it:
- Attempts grading has an option literally called "Last completed attempt" — and Moodle's help text notes it excludes attempts with a "failed" status. A learner whose most recent attempt failed silently gets an older attempt's grade.
- Number of attempts only applies to SCORM 1.2 and AICC packages. It does nothing for SCORM 2004.
Fix: for anything longer than a few minutes, set Force new attempt to No or When previous attempt completed, passed or failed. Reserve Always for short assessments you deliberately want re-taken from scratch.
8. The course can't find the LMS API at all
When a SCORM course launches, it has to locate a JavaScript object provided by the LMS — named API for SCORM 1.2, or API_1484_11 for SCORM 2004. The spec says the course should walk up through parent frames, then check window.opener and walk up from there.
Badly written packages don't. As Rustici put it, some content just searches the frames in the current window and, if it doesn't find the API, quits. There is also a hard recursion limit — the SCORM 1.2 reference implementation gives up after seven levels with the wonderfully unhelpful error "Error finding API -- too deeply nested".
In Moodle this shows up as the classic "works in current window, breaks in new window" bug, because the Display package setting determines whether your content is a child frame or an opener child. Hosting the package on a different domain to the LMS makes it worse, since the browser's same-origin policy blocks the frame walk outright.
Fix: switch Display package to Current window and retest. If that resolves it, the package's API discovery is non-compliant — get it republished. Always host packages on the same origin as the LMS.
9. The activity is ticked but the course still says "In progress" — this one is cron
This is a different problem wearing the same costume, and it is worth separating cleanly.
Activity completion updates in real time. When the SCO writes a qualifying value, Moodle updates grades and completion synchronously on that request.
Course completion does not. Moodle's documentation is explicit: manually marked criteria update immediately, but anything met automatically — passing a quiz, completing a SCORM — updates the next time Moodle's cron runs. The scheduled task is core\task\completion_regular_task, shown in the admin UI as "Calculate regular completion data", and it is set to run every minute on a healthy site.
So: green activity tick, course still "In progress", certificate not issued, restricted section still locked → check cron before you touch a single SCORM setting.
There is a second, subtler timing quirk worth knowing. Moodle only recalculates completion when the SCO writes a score, or writes a status of exactly completed or passed. A package that writes failed or incomplete triggers no recalculation at that moment — which is the mechanism behind the very common report that "it only went green after I went back to the course page."
Bonus: check your Moodle version against these known bugs
Before spending a day on settings, confirm you are not chasing a bug that is already documented. These are the ones that matter:
| Issue | Symptom | Status |
|---|---|---|
| MDL-78518 / MDL-84304 | False completions — activity marked complete after the first quiz question | Fixed in 4.4.9, 4.5.5, 5.0.1 |
| MDL-79967 | Packages that worked on 3.9 stopped completing after the 4.3 database refactor; SCORM logging stopped entirely | Fixed in 4.3.1 |
| MDL-83203 | "Require minimum score" ignored — activity shows criteria unmet but the course reports complete | Still open |
| MDL-71796 | Ticking both Passed and Completed made completion impossible | Fixed in 3.11.1 |
One more thing worth knowing if you write custom reports: Moodle 4.3 replaced the old scorm_scoes_track table with three normalised tables — scorm_attempt, scorm_element and scorm_scoes_value. Any SQL query, plugin or dashboard written against mdl_scorm_scoes_track silently breaks on 4.3 and above. We have migrated more than one client reporting stack because of this.
- 1Read the raw tracking data before changing anything
In the SCORM activity, open Reports → Basic report, then click the number in the Attempts column to see the actual CMI elements stored for that attempt. Look at the status value. If it is anything other than passed or completed, the LMS is behaving correctly and your problem is upstream in the package.
- 2Turn on Moodle's built-in SCORM API debugger
Site administration → Plugins → Activity modules → SCORM package. Enable 'Activate API debug and tracing' and set the API debug capture mask to your own username (for example admin:.*) so you are not dumping debug output for every learner. Relaunch the course and you get a live panel logging every GetValue and SetValue the package makes. This is the single best Moodle-side diagnostic and almost nobody uses it.
- 3Watch the network tab for datamodel.php
Open browser devtools, switch the frame context to the player, and launch the course. You should see POST requests to /mod/scorm/datamodel.php as the learner progresses. No requests at all means the API was never found or Commit is never called. Requests returning errors usually means the Moodle session expired mid-course.
- 4Upload the same package to SCORM Cloud as a neutral referee
SCORM Cloud has a free tier and Rustici built it partly to end vendor finger-pointing. Enable the navigation bar, launch the course, and open View Debug. Check four things: does it call LMSInitialize, does it set the status to completed or passed, does it set a raw score if there is a quiz, and does it call LMSCommit then LMSFinish. All four present means the content is fine and the problem is your LMS. Anything missing means it is the package.
- 5Open the imsmanifest.xml
Unzip the package and read the manifest. Look for adlcp:masteryscore (which lets the LMS override your status), completionThreshold with minProgressMeasure, how many items are marked scormtype='sco' versus 'asset', and which SCORM version the package declares. Five minutes here explains a surprising number of mysteries.
- 6Check cron and your Moodle version last
If the activity is green but the course is not, look at Site administration → Server → Scheduled tasks and confirm 'Calculate regular completion data' is actually running. Then check your exact Moodle point release against the bug table above before concluding it is a configuration problem.
SCORM 1.2 — one flag doing two jobs
- Single element: cmi.core.lesson_status
- Six values: passed, completed, failed, incomplete, browsed, not attempted
- Cannot express 'completed but failed' — there is only one slot
- suspend_data capped at 4,096 characters
- Mastery score in the manifest lets the LMS overwrite your status at exit
- Moodle's 'Number of attempts' setting applies here
- Still the format behind roughly 92% of real course launches
SCORM 2004 — completion and success separated
- Two elements: cmi.completion_status and cmi.success_status
- Completion: completed, incomplete, not attempted, unknown
- Success: passed, failed, unknown
- Can correctly express 'completed but failed'
- suspend_data up to 64,000 characters in 3rd and 4th Edition
- Uses completion_threshold and scaled_passing_score from the manifest
- Better resume behaviour, but only if cmi.exit is set to suspend
Need the course rebuilt properly rather than patched?
Most stuck-incomplete problems trace back to how the package was authored, not how the LMS was configured — and no amount of settings tinkering fixes a course that never sends a status. Our course development team builds SCORM packages that are tested inside the actual LMS they will run in, with completion verified before handover — not shipped as a ZIP file with our fingers crossed. We also take on remediation of existing packages, including legacy courses built in tools that are no longer supported. Links to everything are at the end of this article.
The 60-second triage
Open the Basic report and look at the stored status for one affected learner. If it says passed or completed, the package is fine and the problem is in your Moodle activity settings or cron. If it says anything else — failed, incomplete, browsed — the problem is in the package or the mastery score, and no LMS setting will fix it. That one check splits the problem space in half before you have opened a single setting page.
Frequently asked questions
Why does my SCORM course say incomplete even though the learner finished it?
Because the package sent a status the LMS does not accept as completion. Moodle recognises only passed and completed. If the course reported failed (often because a mastery score overrode it), browsed (the learner used Preview mode), or incomplete (the browser tab was closed before LMSFinish ran), the activity stays incomplete no matter what the learner actually did.
What is the difference between 'Passed' and 'Completed' in Moodle SCORM settings?
Moodle treats them as two separate flags, not synonyms. A package published as 'Passed/Incomplete' sends passed; a package published as 'Completed/Incomplete' sends completed. If your activity's Require status only has one of them ticked and your package sends the other, completion never fires. Tick both — Moodle treats multiple ticks as 'any one of these will do'.
Does closing the browser tab really break SCORM completion?
Yes, and it became much more common after Chrome 80 removed the ability for scripts to make synchronous requests during page unload. Most authoring tools attach the final LMSFinish call to that event. Rustici document the result plainly: the registration can stay incomplete even though the learner finished the course. Enabling Moodle's Auto-commit setting is the most effective defence, because it writes tracking data periodically rather than relying on a final commit that may never arrive.
How do I see the raw SCORM tracking data in Moodle?
Open the SCORM activity, go to the Reports tab, choose Basic report, then click the number in the Attempts column for a learner. That shows the actual CMI elements stored for that attempt — including the status value, raw score and suspend_data. For deeper debugging, enable 'Activate API debug and tracing' in the SCORM plugin settings and set the capture mask to your own username.
Why is my SCORM activity complete but the course still shows in progress?
That is cron, not SCORM. Activity completion updates synchronously when the SCO writes a qualifying status, but course completion aggregation runs on Moodle's scheduled task Calculate regular completion data. On a healthy site that runs every minute; on a site with broken or throttled cron it may never run. Check Site administration → Server → Scheduled tasks before changing any SCORM setting.
Should I publish in SCORM 1.2 or SCORM 2004?
SCORM 1.2 remains the safest default for compatibility — Rustici's 2025 SCORM Cloud data shows roughly 92% of real course launches are still SCORM, with 1.2 holding the largest individual share. Publish in SCORM 2004 3rd or 4th Edition when you specifically need the larger 64,000-character suspend_data limit or genuinely need to separate completion from pass/fail. Keep xAPI available for RFP requirements, but do not lead with it.
Can a mastery score really overwrite a 'completed' status?
Yes. If an adlcp:masteryscore element exists in the manifest, Moodle recalculates the status from the raw score when LMSFinish is called — and Moodle's own help text confirms that any status provided by the package, including 'incomplete', is overridden. A learner scoring 68% against a mastery score of 70 has their completed rewritten to failed, which can never satisfy completion.
The uncomfortable summary
Most "SCORM is broken" tickets are not really LMS problems. They are handover problems — a package built by one team, configured by another, and tested by neither in the environment where it actually has to run. The vendor ships a ZIP file, the LMS admin uploads it, and the first person to discover the completion rule doesn't fire is a learner three weeks into a compliance deadline.
That handover gap is the reason we do both sides. We build SCORM and video courses and we run the Moodle platforms they land in, so completion is verified inside the real LMS with real activity settings before anything is signed off — not assumed because it looked fine in the authoring tool's preview.
A few places to go next:
- Course & content development — SCORM packages, scenario-based modules, AI-assisted training video and multilingual courses, delivered LMS-tested.
- Make existing SCORM interactive with AI — add summaries, flashcards, quizzes, transcripts and ask-the-AI on top of packages you have already paid for, without re-authoring them.
- Moodle DevOps & managed support — for when the real culprit turns out to be cron, sessions or the hosting layer.
Got a package that refuses to complete? Send it over with your Moodle version and the activity's completion settings and we will tell you which of the nine it is — email marketing@edzlms.com.