Released 2026-11-09
Security
Breaking Change: Course and asset imports from Engine's local file system (
file:URLs or local paths infetchRequest,referenceRequest,/courses/importJobs, and assetfetchUrl) are now rejected unless the new system setting ImportLocalFileAllowList lists the directories they may come from. Paths are resolved (including../) before checking and must be one of those directories or inside one.Updated Guava to 33.8.0-jre to address CVE-2026-118042.
Bug Fixes
Fixed an intermittent 500 error ("The given key was not present in the dictionary") from plugin API call - such as LTI 1.3 line items - when a tenant was deleted at the same moment.
[Upgrade] - Fixes problem loading internal schema document when upgrading from 21 and below while relying on the FromMajorVersion setting.
[xAPI] With legacy xAPI OAuth1 support enabled, a statement request whose body claims a form content type but doesn't parse as one now gets a 400 "Bad Request" instead of a 404.
Improvements
When SingleUsageQueryAcrossTenants is "true", gather usage counts across tenants in one query. If unset, SingleUsageQueryAcrossTenants falls back to AllTenantsUseSameDatabaseConnectionInfo.
[Upgrade] - Validates and warns when legacy 'utf8' collation detected on schema.
[Upgrade] - Defer dropping upgrade-specific indexes until the tail-end of the process.
- Two new endpoints were added to allow course metadata to be updated via PATCH requests:
/courses/{courseId}/versions/{versionId}/metadataand/courses/{courseId}/metadata. - [LTI 1.1] - Custom parameter names are now stored and sent in the form the spec describes (lowercase, non-alphanumerics replaced with
_) when Engine adds thecustom_prefix. For example, the supplemental launch parameteruserIdis now sent ascustom_useridinstead ofcustom_userId.