diff --git a/frontends/api/package.json b/frontends/api/package.json
index d76f4d52a5..3247087dd4 100644
--- a/frontends/api/package.json
+++ b/frontends/api/package.json
@@ -29,7 +29,7 @@
"ol-test-utilities": "0.0.0"
},
"dependencies": {
- "@mitodl/mitxonline-api-axios": "2026.5.1",
+ "@mitodl/mitxonline-api-axios": "2026.5.21",
"@tanstack/react-query": "^5.66.0",
"axios": "^1.12.2",
"tiny-invariant": "^1.3.3"
diff --git a/frontends/main/package.json b/frontends/main/package.json
index 6774f02fb2..2ec5327f52 100644
--- a/frontends/main/package.json
+++ b/frontends/main/package.json
@@ -14,7 +14,7 @@
"@emotion/styled": "^11.11.0",
"@floating-ui/react": "^0.27.16",
"@mitodl/course-search-utils": "^3.5.2",
- "@mitodl/mitxonline-api-axios": "2026.5.1",
+ "@mitodl/mitxonline-api-axios": "2026.5.21",
"@mitodl/smoot-design": "^6.27.0",
"@mui/material": "^6.4.5",
"@mui/material-nextjs": "^6.4.3",
diff --git a/frontends/main/src/app-pages/DashboardPage/ContractContent.test.tsx b/frontends/main/src/app-pages/DashboardPage/ContractContent.test.tsx
index 08876330da..6cea57b8dd 100644
--- a/frontends/main/src/app-pages/DashboardPage/ContractContent.test.tsx
+++ b/frontends/main/src/app-pages/DashboardPage/ContractContent.test.tsx
@@ -1910,4 +1910,30 @@ describe("ContractContent", () => {
const coursewareButton = within(card).getByTestId("courseware-button")
expect(coursewareButton).toHaveAttribute("href", enrolledRun.courseware_url)
})
+
+ it("renders without crashing when program.page is null", async () => {
+ const { orgX, programA, programB } = setupProgramsAndCourses()
+ const contract = orgX.contracts[0]
+
+ setMockResponse.get(
+ urls.programs.programsList({
+ org_id: orgX.id,
+ contract_id: contract.id,
+ page_size: 30,
+ }),
+ {
+ results: [
+ { ...programA, page: null },
+ { ...programB, page: null },
+ ],
+ },
+ )
+
+ renderWithProviders(
+ ,
+ )
+
+ await screen.findByText(programA.title)
+ await screen.findByText(programB.title)
+ })
})
diff --git a/frontends/main/src/app-pages/ProductPages/CoursePage.test.tsx b/frontends/main/src/app-pages/ProductPages/CoursePage.test.tsx
index 97bd76d09f..24da3c7eda 100644
--- a/frontends/main/src/app-pages/ProductPages/CoursePage.test.tsx
+++ b/frontends/main/src/app-pages/ProductPages/CoursePage.test.tsx
@@ -483,4 +483,13 @@ describe("CoursePage", () => {
).not.toBeInTheDocument()
})
})
+
+ test("Renders without crashing when course_details.page is null", async () => {
+ const course = makeCourse({ page: null })
+ const page = makePage({ course_details: course })
+ setupApis({ course, page })
+ renderWithProviders()
+
+ await screen.findByRole("heading", { name: page.title })
+ })
})
diff --git a/frontends/main/src/app-pages/ProductPages/CoursePage.tsx b/frontends/main/src/app-pages/ProductPages/CoursePage.tsx
index b011bfe24d..3b1480728c 100644
--- a/frontends/main/src/app-pages/ProductPages/CoursePage.tsx
+++ b/frontends/main/src/app-pages/ProductPages/CoursePage.tsx
@@ -75,13 +75,13 @@ const CoursePage: React.FC = ({ readableId }) => {
}
const imageSrc =
- page.course_details.page.feature_image_src || DEFAULT_RESOURCE_IMG
+ page.course_details.page?.feature_image_src || DEFAULT_RESOURCE_IMG
return (
}
diff --git a/frontends/main/src/app-pages/ProductPages/ProductSummary.test.tsx b/frontends/main/src/app-pages/ProductPages/ProductSummary.test.tsx
index 1741b4d2f7..9185874658 100644
--- a/frontends/main/src/app-pages/ProductPages/ProductSummary.test.tsx
+++ b/frontends/main/src/app-pages/ProductPages/ProductSummary.test.tsx
@@ -1185,6 +1185,13 @@ describe("ProgramSummary", () => {
}
},
)
+
+ test("Does not crash and hides duration row when program page is null", () => {
+ const program = factories.programs.program({ page: null })
+ renderWithProviders()
+
+ expect(screen.queryByTestId(TestIds.DurationRow)).toBeNull()
+ })
})
describe("Pacing Row", () => {
diff --git a/frontends/main/src/app-pages/ProductPages/ProductSummary.tsx b/frontends/main/src/app-pages/ProductPages/ProductSummary.tsx
index 7e4e4a594c..6f9923b0a0 100644
--- a/frontends/main/src/app-pages/ProductPages/ProductSummary.tsx
+++ b/frontends/main/src/app-pages/ProductPages/ProductSummary.tsx
@@ -694,8 +694,8 @@ const ProgramDurationRow: React.FC = ({
program,
...others
}) => {
- const duration = program.page.length ?? ""
- const effort = program.page.effort ?? ""
+ const duration = program.page?.length ?? ""
+ const effort = program.page?.effort ?? ""
if (!duration) return null
const display = [duration, effort].filter(Boolean).join(", ")
@@ -780,10 +780,12 @@ const ProgramCertificateBox: React.FC<{ program: V2ProgramDetail }> = ({
: {formatPrice(price, { avoidCents: true })}
- {program.page.financial_assistance_form_url ? (
+ {program.page?.financial_assistance_form_url ? (
{
).not.toBeInTheDocument()
})
})
+
+ test("Renders without crashing when program_details.page is null", async () => {
+ const program = makeProgramAsCourse({ page: null })
+ const page = makePage({ program_details: program })
+ setupApis({ program, page })
+ renderWithProviders(
+ ,
+ )
+
+ await screen.findByRole("heading", { name: page.title })
+ })
})
diff --git a/frontends/main/src/app-pages/ProductPages/ProgramAsCoursePage.tsx b/frontends/main/src/app-pages/ProductPages/ProgramAsCoursePage.tsx
index b7080c0f2f..deca2685bb 100644
--- a/frontends/main/src/app-pages/ProductPages/ProgramAsCoursePage.tsx
+++ b/frontends/main/src/app-pages/ProductPages/ProgramAsCoursePage.tsx
@@ -258,7 +258,7 @@ const ProgramAsCoursePage: React.FC = ({
}
const imageSrc =
- page.program_details.page.feature_image_src || DEFAULT_RESOURCE_IMG
+ page.program_details.page?.feature_image_src || DEFAULT_RESOURCE_IMG
const dataLoading =
(courseIds.length > 0 && !courses.isSuccess) ||
@@ -271,7 +271,7 @@ const ProgramAsCoursePage: React.FC = ({
shortDescription={
}
imageSrc={imageSrc}
diff --git a/frontends/main/src/app-pages/ProductPages/ProgramPage.test.tsx b/frontends/main/src/app-pages/ProductPages/ProgramPage.test.tsx
index 7246de5194..805a57e11a 100644
--- a/frontends/main/src/app-pages/ProductPages/ProgramPage.test.tsx
+++ b/frontends/main/src/app-pages/ProductPages/ProgramPage.test.tsx
@@ -798,4 +798,13 @@ describe("ProgramPage", () => {
).not.toBeInTheDocument()
})
})
+
+ test("Renders without crashing when program_details.page is null", async () => {
+ const program = makeProgram({ ...makeReqs(), page: null })
+ const page = makePage({ program_details: program })
+ setupApis({ program, page })
+ renderWithProviders()
+
+ await screen.findByRole("heading", { name: page.title })
+ })
})
diff --git a/frontends/main/src/app-pages/ProductPages/ProgramPage.tsx b/frontends/main/src/app-pages/ProductPages/ProgramPage.tsx
index e9c3cfc689..abea97baae 100644
--- a/frontends/main/src/app-pages/ProductPages/ProgramPage.tsx
+++ b/frontends/main/src/app-pages/ProductPages/ProgramPage.tsx
@@ -275,7 +275,7 @@ const ProgramPage: React.FC = ({ readableId }) => {
}
const imageSrc =
- page.program_details.page.feature_image_src || DEFAULT_RESOURCE_IMG
+ page.program_details.page?.feature_image_src || DEFAULT_RESOURCE_IMG
const dataLoading =
(courseIds.length > 0 && !courses.isSuccess) ||
@@ -289,7 +289,7 @@ const ProgramPage: React.FC = ({ readableId }) => {
shortDescription={
}
imageSrc={imageSrc}
diff --git a/yarn.lock b/yarn.lock
index d2ac5fe0cb..3177d89e84 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -3332,13 +3332,13 @@ __metadata:
languageName: node
linkType: hard
-"@mitodl/mitxonline-api-axios@npm:2026.5.1":
- version: 2026.5.1
- resolution: "@mitodl/mitxonline-api-axios@npm:2026.5.1"
+"@mitodl/mitxonline-api-axios@npm:2026.5.21":
+ version: 2026.5.21
+ resolution: "@mitodl/mitxonline-api-axios@npm:2026.5.21"
dependencies:
"@types/node": "npm:^20.11.19"
axios: "npm:^1.6.5"
- checksum: 10/6eb179298221fc2801ce4e3c0589de0378c68b7a32503101de675a85f6e3569c78ec6cb4b1bc5aa85094637532a34d10c110a4a9cdfa96c525dc4362e0236d5c
+ checksum: 10/01f91379031fff433fe511ad7fd49f7b0f77d4d45c22c693ab07e191586c98f0e90ba15bca966a3a5cba0039e1a1b85fa776128742a84b626fb6c736270df7f3
languageName: node
linkType: hard
@@ -8939,7 +8939,7 @@ __metadata:
resolution: "api@workspace:frontends/api"
dependencies:
"@faker-js/faker": "npm:^10.0.0"
- "@mitodl/mitxonline-api-axios": "npm:2026.5.1"
+ "@mitodl/mitxonline-api-axios": "npm:2026.5.21"
"@tanstack/react-query": "npm:^5.66.0"
"@testing-library/react": "npm:^16.3.0"
axios: "npm:^1.12.2"
@@ -16184,7 +16184,7 @@ __metadata:
"@floating-ui/react": "npm:^0.27.16"
"@happy-dom/jest-environment": "npm:^20.1.0"
"@mitodl/course-search-utils": "npm:^3.5.2"
- "@mitodl/mitxonline-api-axios": "npm:2026.5.1"
+ "@mitodl/mitxonline-api-axios": "npm:2026.5.21"
"@mitodl/smoot-design": "npm:^6.27.0"
"@mui/material": "npm:^6.4.5"
"@mui/material-nextjs": "npm:^6.4.3"