Verifiable Credentials QR Code Scan Issue - Stuck at Credential Storage Step

I’m working on implementing verifiable credentials following the official documentation. I’ve successfully completed most of the setup, but am encountering an issue at the final step.

What I’ve Completed Successfully

  1. :white_check_mark: Followed the Verifiable Credentials Quickstart guide
  2. :white_check_mark: Completed the full setup process
  3. :white_check_mark: Can view course certificates in the Verifiable Credentials app
  4. :white_check_mark: QR code displays correctly in the modal
  5. :white_check_mark: Made an entry to the sandbox-registry repo: registry.json#L164-L168
    "did:key:z6Mkiq1TLJRWVBt4pdaXv4CCEasnVpoj2qJrnGEJ541L6MJQ": {
      "name": "Wikimedia Foundation (Test Issuer)",
      "location": "San Francisco, CA, USA",
      "url": "https://learnwiki-dev.edly.io/"
    }

The Problem

I’m stuck at Step 4 of the Credential Storage documentation.

Issue: When I scan the QR code with the wallet app, it shows “Handling credentials request”, but then nothing happens. The credential is not being stored in the wallet.

Server Error

After checking the credentials service logs, I found the following error occurring when the wallet app attempts to retrieve the credentials:

credentials-1  | 2026-01-29 13:54:11,815 ERROR 8 [django.request] log.py:241 - Internal Server Error: /verifiable_credentials/api/v1/credentials/issue/bdbff49a-442e-45b1-a8a6-356897ba46c9/
credentials-1  | Traceback (most recent call last):
credentials-1  |   File "/openedx/venv/lib/python3.11/site-packages/django/core/handlers/exception.py", line 55, in inner
credentials-1  |     response = get_response(request)
credentials-1  |                ^^^^^^^^^^^^^^^^^^^^^
credentials-1  |   File "/openedx/venv/lib/python3.11/site-packages/django/core/handlers/base.py", line 197, in _get_response
credentials-1  |     response = wrapped_callback(request, *callback_args, **callback_kwargs)
credentials-1  |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
credentials-1  |   File "/openedx/venv/lib/python3.11/site-packages/django/views/decorators/csrf.py", line 56, in wrapper_view
credentials-1  |     return view_func(*args, **kwargs)
credentials-1  |            ^^^^^^^^^^^^^^^^^^^^^^^^^^
credentials-1  |   File "/openedx/venv/lib/python3.11/site-packages/django/views/generic/base.py", line 104, in view
credentials-1  |     return self.dispatch(request, *args, **kwargs)
credentials-1  |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
credentials-1  |   File "/openedx/venv/lib/python3.11/site-packages/rest_framework/views.py", line 515, in dispatch
credentials-1  |     response = self.handle_exception(exc)
credentials-1  |                ^^^^^^^^^^^^^^^^^^^^^^^^^^
credentials-1  |   File "/openedx/venv/lib/python3.11/site-packages/rest_framework/views.py", line 475, in handle_exception
credentials-1  |     self.raise_uncaught_exception(exc)
credentials-1  |   File "/openedx/venv/lib/python3.11/site-packages/rest_framework/views.py", line 486, in raise_uncaught_exception
credentials-1  |     raise exc
credentials-1  |   File "/openedx/venv/lib/python3.11/site-packages/rest_framework/views.py", line 503, in dispatch
credentials-1  |     self.initial(request, *args, **kwargs)
credentials-1  |   File "/openedx/venv/lib/python3.11/site-packages/rest_framework/views.py", line 421, in initial
credentials-1  |     self.check_permissions(request)
credentials-1  |   File "/openedx/venv/lib/python3.11/site-packages/rest_framework/views.py", line 338, in check_permissions
credentials-1  |     if not permission.has_permission(request, self):
credentials-1  |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
credentials-1  |   File "/openedx/venv/lib/python3.11/site-packages/rest_framework/permissions.py", line 87, in has_permission
credentials-1  |     self.op2.has_permission(request, view)
credentials-1  |   File "/openedx/credentials/credentials/apps/verifiable_credentials/permissions.py", line 60, in has_permission
credentials-1  |     result = didkit_verify_presentation(presentation_json, proof_options_json)
credentials-1  |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
credentials-1  |   File "/openedx/venv/lib/python3.11/site-packages/asgiref/sync.py", line 254, in __call__
credentials-1  |     return call_result.result()
credentials-1  |            ^^^^^^^^^^^^^^^^^^^^
credentials-1  |   File "/opt/pyenv/versions/3.11.9/lib/python3.11/concurrent/futures/_base.py", line 449, in result
credentials-1  |     return self.__get_result()
credentials-1  |            ^^^^^^^^^^^^^^^^^^^
credentials-1  |   File "/opt/pyenv/versions/3.11.9/lib/python3.11/concurrent/futures/_base.py", line 401, in __get_result
credentials-1  |     raise self._exception
credentials-1  |   File "/openedx/venv/lib/python3.11/site-packages/asgiref/sync.py", line 331, in main_wrap
credentials-1  |     result = await self.awaitable(*args, **kwargs)
credentials-1  |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
credentials-1  |   File "/openedx/credentials/credentials/apps/verifiable_credentials/issuance/utils.py", line 97, in didkit_verify_presentation
credentials-1  |     return await didkit.verify_presentation(
credentials-1  |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
credentials-1  | ValueError: Invalid context at line 1 column 105

The error occurs at the /verifiable_credentials/api/v1/credentials/issue/{uuid}/ endpoint during the permission check phase, specifically when didkit_verify_presentation is called.

Questions

  1. Has anyone encountered the `ValueError: Invalid context at line 1 column 105` error when setting up verifiable credentials?
  2. Are there any common configuration issues at this step that I might be missing?
  3. Could this be related to my sandbox registry entry configuration? Should the context URLs in my registry match specific values?

Environment Details

  • Tutor: 20.0.1
  • tutor-credentials: 20.0.0
  • edx-platform fork: teak
  • Python: 3.11.9

Any guidance or suggestions would be greatly appreciated! Thanks in advance for your help.

@Glib_Glugovskiy - any advice here? Thank you!

@Ahmed_Khalid Thanks for providing all the details.

I am currently setting up the same version on my side to test it. But from the first glance the issue seems to be in the communication flow between Open edX and LC Wallet.

By the way, could you please specify the OS version of LC Wallet and your mobile OS?

@Ahmed_Khalid Ok, I tested different cases, and unfortunately this is an actual bug in integration with LC Wallet.

The setup you’ve done is totally correct. I get the same error in the didkit_verify_presentationfunction using the latest LC Wallet application. But when I tried to downgrade the version using a publicly available android APK I was able to do the whole issuance flow to the mobile wallet correctly. My testing results are the following:

  • LCW v2.0.0 - Includes a bug that fails signature verification.
  • LCW <v2.1 - Work correctly with OeX implementation.
  • LCW >=v2.1.5 - All have this error with presentation verification.

While this makes it impossible to use VCs in production, for demo purposes you may use this workaround with downgrade. Meanwhile, I will investigate if any fix can be done there and included as a patch for the Teak version.

@Glib_Glugovskiy Thank you for your input on this. I’m currently on LCW v2.2.9 with Android 16. I’ll try the LCW version you suggested. That should unblock me for now.

Downgrading the LCW version to 20.0.23 got me past the VC generation step. But now I am getting that it “has been revoked by issuer”.

Even though in my admin panel, I see that it is not revoked:

I don’t know if it’s helpful, but this is the source of the generated VC:

{
  "@context": [
    "https://www.w3.org/2018/credentials/v1",
    "https://w3id.org/security/suites/ed25519-2020/v1",
    "https://w3id.org/vc/status-list/2021/v1",
    "https://purl.imsglobal.org/spec/ob/v3p0/context.json"
  ],
  "id": "urn:uuid:05f427bb-5036-4741-9159-dbab28899fcf",
  "type": [
    "VerifiableCredential",
    "OpenBadgeCredential"
  ],
  "credentialSubject": {
    "id": "did:key:z6MkfVgLDotNpWRcHJCJm2GHzbNLTn4G8C6rzR1Gf8qyiBKR",
    "name": "Ahmed-arbisoft",
    "type": "AchievementSubject",
    "achievement": {
      "criteria": {
        "narrative": "Ahmed-arbisoft successfully completed a course and received a passing grade for a Course Certificate in certificate testing course a course offered by edly, in collaboration with Wikilearn Dev. "
      },
      "description": "Course certificate is granted on course certificate testing course completion offered by edly, in collaboration with Wikilearn Dev",
      "id": "228ebc2f-8f7b-4f3a-9e99-1b4763a4103b",
      "name": "course certificate",
      "type": "Achievement"
    }
  },
  "issuer": {
    "id": "did:key:z6Mkiq1TLJRWVBt4pdaXv4CCEasnVpoj2qJrnGEJ541L6MJQ",
    "name": "Wikimedia Foundation (Test Issuer)",
    "type": "Profile"
  },
  "issuanceDate": "2026-01-30T13:42:56Z",
  "proof": {
    "type": "Ed25519Signature2020",
    "proofPurpose": "assertionMethod",
    "proofValue": "z2s5r65qHzfdA23G1oyfgFagUUUHxpx4suT3sZAWTwpeLXCGSkkBaApv9D8Lhfdbj9FjzmE1fi2LeCgVhWb95QZ3x",
    "verificationMethod": "did:key:z6Mkiq1TLJRWVBt4pdaXv4CCEasnVpoj2qJrnGEJ541L6MJQ#z6Mkiq1TLJRWVBt4pdaXv4CCEasnVpoj2qJrnGEJ541L6MJQ",
    "created": "2026-01-30T13:42:56.412Z"
  },
  "credentialStatus": {
    "id": "https://credentials.learnwiki-dev.edly.io/verifiable_credentials/api/v1/status-list/2021/v1/did:key:z6Mkiq1TLJRWVBt4pdaXv4CCEasnVpoj2qJrnGEJ541L6MJQ/#2",
    "type": "StatusList2021Entry",
    "statusPurpose": "revocation",
    "statusListIndex": "2",
    "statusListCredential": "https://credentials.learnwiki-dev.edly.io/verifiable_credentials/api/v1/status-list/2021/v1/did:key:z6Mkiq1TLJRWVBt4pdaXv4CCEasnVpoj2qJrnGEJ541L6MJQ/"
  },
  "validFrom": "2026-01-30T13:42:56Z",
  "name": "course certificate",
  "issued": "2026-01-30T13:42:56Z"
}

And the credentials logs:

credentials-1  | [pid: 8|app: 0|req: 270/734] 172.18.0.2 () {40 vars in 744 bytes} [Fri Jan 30 14:28:48 2026] GET /verifiable_credentials/api/v1/status-list/2021/v1/did:key:z6Mkiq1TLJRWVBt4pdaXv4CCEasnVpoj2qJrnGEJ541L6MJQ/ => generated 1202 bytes in 62 msecs (HTTP/1.1 200) 6 headers in 192 bytes (1 switches on core 0)

@Ahmed_Khalid I added the fix for this to master in commit 0b66761.
You can apply this patch and rebuild the credentials image. Afterward, the format of the StatusList will be consistent with this version of the LC Wallet, and all issued credentials should have a verified status.

@Glib_Glugovskiy Thanks a bunch for this fix! Can you please also backport it to Teak?

@Ahmed_Khalid Sure, I want to research this week if there’s a solution to solve the issue in integration with the latest LCW app version, and afterward I’ll open a patch for the Teak version of the tutor-credentials plugin.

1 Like

Sounds good. Thanks for your help on this.