From 5c6e6e9944579378cd48495bdbb0688db20b9ba1 Mon Sep 17 00:00:00 2001 From: Andrew Scholer Date: Thu, 17 Sep 2026 14:51:50 -0700 Subject: [PATCH 1/2] LTI1p3: Verbose debugging for blackboard intrgration issue --- .../rsptx/lti1p3/pylti1p3/service_connector.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/components/rsptx/lti1p3/pylti1p3/service_connector.py b/components/rsptx/lti1p3/pylti1p3/service_connector.py index 8fbedade8..bf5bf14e9 100644 --- a/components/rsptx/lti1p3/pylti1p3/service_connector.py +++ b/components/rsptx/lti1p3/pylti1p3/service_connector.py @@ -11,6 +11,7 @@ import typing_extensions as te from .exception import LtiServiceException, LtiException from .registration import Registration +from rsptx.logging import rslogger TServiceConnectorResponse = te.TypedDict( "TServiceConnectorResponse", @@ -113,9 +114,15 @@ async def get_access_token(self, scopes: t.Sequence[str]) -> str: try: r = await self._requests_session.post(auth_url, data=auth_request) if not r.ok: + rslogger.error( + f"get_access_token post failed: {r.status} - {r.reason}. Headers: {r.headers}. Full response: {r.__dict__}" + ) raise LtiServiceException(r) except Exception: raw_body = await r.text() + rslogger.error( + f"get_access_token exception caught: {r.status} - {r.reason}. Headers: {r.headers}. Full response: {r.__dict__}" + ) raise LtiServiceException(r) if r.content_type == "application/json": response = await r.json() @@ -127,6 +134,9 @@ async def get_access_token(self, scopes: t.Sequence[str]) -> str: response = json.loads(raw_body) except json.JSONDecodeError: r.reason = "JSON decode error" + rslogger.error( + f"get_access_token json decode error: {r.status} - {r.reason}. Headers: {r.headers}. Full response: {r.__dict__}" + ) raise LtiServiceException(r) self._access_tokens[scope_key] = response["access_token"] @@ -182,6 +192,9 @@ async def make_service_request( raise LtiException("Unsupported HTTP method: " + method) if not r.ok: + rslogger.error( + f"Service request failed: {r.status} - {r.reason}. Headers: {r.headers}. Full response: {r.__dict__}" + ) raise LtiServiceException(r) next_page_url = None From 4258becc9c62f3eed4f8cb1d96d710012067812c Mon Sep 17 00:00:00 2001 From: Andrew Scholer Date: Thu, 17 Sep 2026 15:06:05 -0700 Subject: [PATCH 2/2] LTI1p3: Clarify possible cause of failure to access line items --- bases/rsptx/admin_server_api/routers/lti1p3.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bases/rsptx/admin_server_api/routers/lti1p3.py b/bases/rsptx/admin_server_api/routers/lti1p3.py index a528a1c74..2f1d95a27 100644 --- a/bases/rsptx/admin_server_api/routers/lti1p3.py +++ b/bases/rsptx/admin_server_api/routers/lti1p3.py @@ -964,7 +964,7 @@ async def dynamic_link_entry(request: Request): rslogger.error(f"LTI1p3 - Error accessing course line items: {e}") raise HTTPException( status_code=422, - detail="This course appears to be closed in your Learning Management System.", + detail=f"Error accessing your course's assignments. Your course may be closed in your Learning Management System. Debugging information: {e}", ) l_items.sort(key=lambda li: li.get("label"))