Fixing iSpring + SQL-LRS: a small xAPI handshake problem
Some problems don’t look big at first.
The LRS is running.
Statements are coming in.
The content launches fine.
And then:
- resume stops working
- state writes start failing
- “Introduce yourself” shows up again
- the network tab fills with
401and409
This post is about one of those problems, and a small shim that eventually fixed it.
The context
I was integrating iSpring xAPI content into a broader learning platform backed by SQL-LRS (Yet Analytics), with Moodle sitting around it.
On paper, this should be straightforward:
- iSpring supports xAPI
- SQL-LRS is spec-correct
- Moodle just launches the content
In practice, the behavior was inconsistent and hard to reason about.
The symptom
The pattern looked like this:
- First launch sometimes worked
- Subsequent launches didn’t
PUT /activities/statereturned409 Conflict- Sometimes it degraded into
401 Unauthorized - Resume never became reliable
Nothing else in the system seemed broken.
What was actually happening
SQL-LRS enforces xAPI state concurrency correctly.
That means:
- State updates require
If-Matchwith a validETag - Or
If-None-Match: *when creating state for the first time
Some xAPI clients (including iSpring) don’t fully implement this negotiation.
They perform blind PUT requests and assume the server will accept them.
SQL-LRS doesn’t — and shouldn’t.
So this wasn’t really a “bug” on either side.
It was a mismatch in expectations.
Why Moodle couldn’t fix this
The failure happens inside the browser, between the content player and the LRS.
Moodle never sees these requests.
It can’t inject headers or fix concurrency logic after the fact.
This had to be solved at the protocol boundary.
The idea: a small compatibility shim
Instead of relaxing SQL-LRS, I added a very small proxy in front of it.
The shim:
- Handles ETag negotiation
- Performs a GET-before-PUT when needed
- Uses its own trusted credentials to talk to the LRS
- Leaves everything else untouched
No changes to:
- iSpring packages
- Moodle configuration
- SQL-LRS settings
What changed after that
Once the shim was in place:
- No more
401 - No more
409 - Resume started working
- State persisted reliably
- SQL-LRS remained strict and spec-correct
The system became boring again — which is usually a good sign.
Why I open-sourced it
This problem isn’t rare.
It’s just not documented very well.
If you’re combining:
- iSpring
- SQL-LRS
- Moodle
- or any strict xAPI LRS
you’ll likely run into the same edge case at some point.
So I made the shim public.
The project
https://github.com/sachinravindran/xapi-shim
It’s small, focused, and intentionally unexciting.
It exists to let different parts of a learning stack cooperate without weakening each other.
A closing thought
Most integration issues aren’t caused by bad software.
They’re caused by two correct systems making different assumptions.
Sometimes the right fix isn’t to change either one —
it’s to add a quiet handshake in between.
This shim is one such handshake.