Skip to content

Display backend error message on enrollment failure - #3918

Merged
rhysyngsun merged 3 commits into
mainfrom
nl/show-enrollment-error-detail
Oct 7, 2026
Merged

rhysyngsun merged 3 commits into
mainfrom
nl/show-enrollment-error-detail

Conversation

@rhysyngsun

@rhysyngsun rhysyngsun commented Sep 8, 2026 •

Copy link
Copy Markdown
Contributor

What are the relevant tickets?

https://github.com/mitodl/hq/issues/13250

Description (What does it do?)

MITx Online answers a rejected enrollment with a 400 whose detail is written for the learner, but Learn discarded the body and rendered the same generic "please try again" for every failure — including ones that cannot succeed on retry.

  • On a 400, enrollment surfaces now show the response's detail.
  • On anything else — a 500, a network error, a 400 with no usable detail — today's copy is unchanged.
  • Applies to the product-page enroll areas, the header CTA, the enrollment dialog, and the dashboard enroll toasts. Deliberately scoped to enrollment: the global toast handler is untouched, so no other backend 400 string becomes user-visible copy.

Pairs with mitodl/mitxonline#3938, which gives compliance rejections a detail worth showing. Neither depends on the other landing first.

Implementation details

New frontends/api/src/mutations/errorMessages.ts, exported as api/mutation-errors:

  • badRequestDetail(error) — the detail string from a 400 body, else undefined. Returns undefined (not a fallback) so each call site keeps its own copy via ?? FALLBACK. Guards a non-axios error, a missing response, any status but 400, a non-object body, and a detail that is missing/blank/non-string; joins a DRF ErrorDetail list. Never throws — it runs inside the global mutation-error handler, where an exception would swallow the failure entirely.
  • badRequestDetailOr(fallback) — builds a MutationErrorMeta["getErrorMessage"]. This is the first production use of that hook; mutationMeta.ts documents it for exactly this case. The no-arg form returns "", which fails the handler's isMessage check and falls through to the existing generic copy rather than restating it.

useCourseEnrollment, useProgramEnrollment, and useReplaceBasketItem now expose error: unknown in place of isError: boolean. error != null is isError, so keeping both would be two derived fields that have to agree. In useReplaceBasketItem the add-to-basket error wins over clear-basket: it is the later step, reset() means the two are never errored from one attempt, and its 400 carries the product-specific explanation.

The dialog keeps two distinct fallbacks (basket vs. enroll), which is why the hooks return the error object rather than a pre-resolved string.

Pre-existing and untouched, but more visible now that the copy is specific: a failed paid attempt keeps its alert up while the user clicks the free CTA. Worth a follow-up.

Screenshots (if appropriate):

Screenshot 2026-09-14 at 14-17-52 Local Dev Course 300 (elective audit only) MIT Learn

How can this be tested?

End-to-end against a local MITx Online: enable the EXPORT_COMPLIANCE_CHECK_ENABLED flag and make verify_user_with_exports return a non-accepted result, then click "Start Learning" on a course product page. I have not run this — the evidence above is unit-level only.

Additional Context

On the dashboard upgrade path, HomeEnrollmentsDisplay and ProgramEnrollmentDisplay append " Contact Support for assistance." to whatever message arrives, so a server detail now renders as "<detail> Contact Support for assistance.". Reads fine for MITx Online's details (they end in punctuation), but it is server-controlled text feeding client-side copy — flagging rather than plumbing a flag through.

@github-actions

github-actions Bot commented Sep 8, 2026 •

Copy link
Copy Markdown

OpenAPI Changes

No changes detected

View full changelog

Unexpected changes? Ensure your branch is up-to-date with main (consider rebasing).

@rhysyngsun rhysyngsun changed the title Show the API's 400 detail on enrollment surfaces Display backend error message on enrollment failure Sep 9, 2026
@rhysyngsun
rhysyngsun force-pushed the nl/show-enrollment-error-detail branch from d5fdd27 to 83be81e Compare September 14, 2026 18:23
@rhysyngsun
rhysyngsun marked this pull request as ready for review September 14, 2026 18:35
Copilot AI balanced review requested due to automatic review settings September 14, 2026 18:35
@rhysyngsun
rhysyngsun requested a review from a team as a code owner September 14, 2026 18:35

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

The scoped error handling preserves existing fallbacks and is supported by focused unit and integration coverage.

Pull request overview

Surfaces learner-facing MITx Online 400-level enrollment errors while preserving existing fallback messaging for other failures.

Changes:

  • Adds reusable extraction of valid 400 detail messages.
  • Propagates mutation errors through enrollment hooks and UI surfaces.
  • Adds coverage for backend details, fallbacks, and basket error precedence.
File summaries
File Description
frontends/api/package.json Exports the mutation-error utility.
frontends/api/src/mutations/errorMessages.ts Extracts safe 400 response details.
frontends/api/src/mutations/errorMessages.test.ts Tests detail extraction and fallbacks.
frontends/main/src/common/mitxonline/useReplaceBasketItem.ts Exposes the relevant basket error.
frontends/main/src/common/mitxonline/useReplaceBasketItem.test.tsx Tests basket error precedence.
frontends/main/src/page-components/EnrollmentDialogs/CourseEnrollmentDialog.tsx Displays backend enrollment details.
frontends/main/src/page-components/EnrollmentDialogs/CourseEnrollmentDialog.test.tsx Tests dialog error messaging.
frontends/main/src/app-pages/ProductPages/useCourseEnrollment.ts Returns course enrollment errors.
frontends/main/src/app-pages/ProductPages/useProgramEnrollment.ts Returns program enrollment errors.
frontends/main/src/app-pages/ProductPages/HeaderEnrollButton.tsx Renders detailed header errors.
frontends/main/src/app-pages/ProductPages/EnrollOfferingBoxes.tsx Renders detailed offering errors.
frontends/main/src/app-pages/ProductPages/enrollTypes.ts Centralizes fallback copy.
frontends/main/src/app-pages/ProductPages/CoursePage.tsx Passes course errors to the header.
frontends/main/src/app-pages/ProductPages/CoursePage.test.tsx Tests course-header error details.
frontends/main/src/app-pages/ProductPages/CourseEnrollArea.tsx Passes course errors to offering boxes.
frontends/main/src/app-pages/ProductPages/CourseEnrollArea.test.tsx Tests paid-enrollment details.
frontends/main/src/app-pages/ProductPages/ProgramHeaderEnrollButton.tsx Passes program errors to the header.
frontends/main/src/app-pages/ProductPages/ProgramHeaderEnrollButton.test.tsx Tests program-header messages.
frontends/main/src/app-pages/ProductPages/ProgramEnrollArea.tsx Passes program errors to offering boxes.
frontends/main/src/app-pages/DashboardPage/CoursewareDisplay/hooks/useEnrollmentHandler.ts Configures dashboard toast messages.
frontends/main/src/app-pages/DashboardPage/CoursewareDisplay/EnrolledCourseCard.tsx Displays detailed upgrade failures.
frontends/main/src/app-pages/DashboardPage/CoursewareDisplay/UnenrolledCourseCard.test.tsx Tests detailed dashboard toasts.
Review details
  • Files reviewed: 22/22 changed files
  • Comments generated: 0
  • Review effort level: Balanced

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@rhysyngsun
rhysyngsun force-pushed the nl/show-enrollment-error-detail branch from 83be81e to 4758b9b Compare September 22, 2026 20:32
rhysyngsun and others added 3 commits October 5, 2026 09:19
MITx Online answers a rejected enrollment with a 400 whose detail is
written for the learner, but Learn discarded the body and rendered the
same generic "please try again" for every failure — including ones that
cannot succeed on retry.

Adds badRequestDetail/badRequestDetailOr, which read a detail only from
a 400 and return undefined otherwise, so a 500 or a detail-less 400
keeps today's copy. The enroll hooks now expose the error object rather
than an isError boolean, and the enroll areas, header CTA, enrollment
dialog, and dashboard toasts prefer the server's string over their own.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
An enrollment 400 detail is often only actionable by a human ("Error code:
CS_700"), but no enrollment error surface offered a way to reach support.

Add ErrorMessageWithSupport, which appends a "Contact Support" link to a
message, and use it on every enrollment failure surface: the product page
InfoBox and header alerts, both enrollment dialog alerts, and - via a new
opt-in `meta.contactSupport` read by the global mutation-error handler - the
dashboard enrollment toast.

The link points at the support site's request form (new urls.SUPPORT_REQUEST)
rather than the mailto: used by the dashboard's upgrade banners, which are
left as they are.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@rhysyngsun
rhysyngsun force-pushed the nl/show-enrollment-error-detail branch from 4758b9b to a877204 Compare October 5, 2026 13:19
@gumaerc gumaerc self-assigned this Oct 6, 2026

@gumaerc gumaerc left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tested this fairly exhaustively. I set my local mitxonline to the branch from mitodl/mitxonline#3938, manually set my user to have a denied compliance check and exercised the errors in the frontend everywhere I thought they could appear. Everything on the product pages seems to be working. There were a couple edge cases I discovered, but I think the fault there lies in mitxonline and not Learn. I will leave a comment on the parent issue about that, but I think this is good to merge. I left one nitpick about some punctuation below:

Comment thread frontends/api/src/mutations/errorMessages.ts
@rhysyngsun
rhysyngsun merged commit 92ba45a into main Oct 7, 2026
14 checks passed
@rhysyngsun
rhysyngsun deleted the nl/show-enrollment-error-detail branch October 7, 2026 11:06
@odlbot odlbot mentioned this pull request Oct 7, 2026
3 of 4 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants