Skip to main content

Troubleshooting

Find the symptom, then apply the fix. Each cookbook lists only the problems that are specific to its steps; everything else is collected here.

When a wallet is involved, collect its log right after the failure: in the EU Reference Implementation open Setting → Retrieve Logs, in Paradym Wallet open Settings → Export Logs. The wallet compatibility record has details for other wallets. On the EUDIPLO side, Sessions → All Sessions in the Web Client shows the status, and GET /api/session/{id} adds failureCode and errorReason. Remove tokens, keys and personal data before you share logs.

Install and connect​

SymptomCauseFix
eudiplo: command not found after the installer ran~/.local/bin is not on your PATHAdd export PATH="$HOME/.local/bin:$PATH" to your shell profile and open a new shell.
Installer stops with "npm is required"No standalone build for this platform (for example an Intel Mac); the installer falls back to npmInstall Node.js 22.12 or later, then rerun the installer, or use npx @eudiplo/cli.
Docker or Podman was not foundNo container runtime on the PATH, or it is not runningInstall and start Docker or Podman; set EUDIPLO_CONTAINER_RUNTIME=docker or podman to choose one.
"port is already allocated" when startingAnother EUDIPLO stack, often eudiplo demo, uses port 3000 or 4200Stop it, for example eudiplo down --instance local for the demo, then run eudiplo up --instance <name>.
Unknown instance or a command acts on the wrong stackThe CLI default instance is a different oneRun eudiplo instance list and pass --instance <name>, or make it the default with eudiplo instance use <name>.
An edit to .eudiplo.env has no effectThe running container still has the old environmentRun eudiplo up --instance <name>; Compose recreates changed containers. eudiplo restart does not reload the environment.
Backend exits right after startInvalid environment, for example a malformed CORS_ORIGINS or a missing secretRead eudiplo logs --instance <name> --service eudiplo --tail 100; the first error names the variable.
localhost health works, the phone cannot reach the backendThe tunnel stopped, forwards to another port, or shows a confirmation pageRestart the tunnel to port 3000. Open https://<public-host>/health in the phone's browser; it must show the JSON directly.

Login​

SymptomCauseFix
Web Client login hangs or reports a connection errorEUDIPLO Instance points to an address the browser cannot reach, such as the prefilled http://eudiplo:3000Enter the backend address as your browser reaches it, for example http://localhost:3000. A trailing slash is removed automatically.
Login rejected with valid-looking credentialsWrong client ID or secreteudiplo demo uses root / root; eudiplo init generates the secret, see AUTH_CLIENT_SECRET in .eudiplo.env. Tenant clients: rotate the secret under Administration → API Clients.
The token endpoint always answers 401The instance uses an external OIDC provider such as KeycloakUse the SSO tab, or get tokens from the identity provider. See Keycloak SSO.
Signed in, but menu sections are missingThe client lacks the roles for themAdd the roles to the client, then sign in again; tokens keep the roles they were issued with. See Tenants and access.
API calls return 403The token lacks the role of the endpoint, for example issuance:offerGive the client the documented role. Never give a tenant client tenants:manage.
Browser console shows a CORS error for /api/...CORS_ORIGINS does not include the page's originAdd the exact origin (scheme://host[:port], no trailing slash) and run eudiplo up.

Wallet connection​

SymptomCauseFix
Wallet cannot open the offer or requestPUBLIC_URL is not reachable from the phone, or it changed after the offer was createdCheck the health endpoint on the phone. Correct PUBLIC_URL, run eudiplo up, and create a new offer.
Offers or metadata contain localhostPUBLIC_URL is still the defaultSet PUBLIC_URL to the public HTTPS address and run eudiplo up.
Wallet rejects the issuer or verifier certificateThe wallet does not accept self-signed certificates, or needs registrar certificatesFollow Wallet and registrar requirements. Do not weaken verification to get around it.
Wallet reports invalid_dpop_proof or a DPoP errorThe wallet does not send a valid DPoP proof, or the proof's key differs from the one bound earlierUse a wallet with DPoP support, or relax the DPoP setting of the issuer. See Authorization servers.
Wallet reports a PKCE or invalid_request error at authorizationSince 9.0, authorization codes require PKCE with S256Use a wallet that sends an S256 code challenge.

Issuance​

SymptomCauseFix
Session stays activeThe wallet never fetched the offerSee Wallet connection.
Session stays fetchedThe credential was delivered, but the wallet sent no notificationNothing to fix; only a credential_accepted notification moves the session to completed.
POST /api/issuer/offer returns 400 for the claimsThe claims do not match the claim fields of the credential configurationSend every mandatory claim with the configured type. See Claims.
Wallet gets invalid_grant or "The session has expired"The offer outlived its lifetimeCreate a new offer; adjust offerLifetimeSeconds if needed.
Wallet asks for a key attestationThe credential's supported proof types require oneAdd proof type JWT, or use a wallet with key attestation.
"No key chain found with usage type ..."The tenant has no key chain for that purposeCreate one under Cryptographic Assets → Keys.
Attribute provider or webhook is never calledThe URL is HTTP or private, which the outbound URL policy blocks by defaultUse a public HTTPS URL. For local development only, set OUTBOUND_URL_ALLOW_HTTP=true and OUTBOUND_URL_ALLOW_PRIVATE_NETWORK=true.
"Failed to fetch authorization server metadata" or "Failed to fetch upstream OIDC configuration"The external authorization server or the chained server's upstream provider is on HTTP or a private address, which the outbound URL policy blocks by default; the log names the rejected URLUse a public HTTPS URL. For local development only, set OUTBOUND_URL_ALLOW_HTTP=true and OUTBOUND_URL_ALLOW_PRIVATE_NETWORK=true.
The credential in the wallet shows old valuesA wallet keeps the claims it was issued withIssue a new credential after changing configuration or claims.

Presentation​

SymptomCauseFix
Wallet finds no matching credentialFormat, VCT or claim paths differ from the issued credential, or trusted_authorities excludes its issuerCompare the DCQL query with the credential type. See DCQL.
Wallet rejects the verifierThe access certificate is missing, inactive or not trusted by the walletSelect an active access key chain in the presentation configuration; see Wallet and registrar requirements.
Request rejected for overaskingThe registration certificate does not cover every requested credential and claimUpdate the registration certificate to match the DCQL query. See Registration certificates.
Wallet gets "The session has expired"The request outlived its lifetime (default 300 seconds)Generate a new request and approve it sooner, or raise the lifetime.
Session failed with access_deniedThe user declined in the walletExpected; offer a new request.
Session failed with trust_chain_not_trusted or no_trust_chain_to_rootThe issuer is not on the trust lists of the credential queryAdd the issuer to the trust list, or accept that the credential is rejected. See Accept only trusted issuers.
Session failed with trust_list_unavailableA trust list could not be loaded (also when the outbound URL policy blocks its HTTP or private URL), has expired, or lacks verification materialCheck the list URL, the backend log and its verifierX509Der or verifierKey. See Trust lists.
Session failed with "Status is not valid"The credential is revoked or suspendedExpected after revocation. See Revocable credentials.
No webhook arrivesThe endpoint is unreachable or blocked by the outbound URL policy; webhooks are not retriedCheck the backend log, then read the result with GET /api/session/{id}. See Receive results.
Session shows active after its expiry timeThe expired status is written by a periodic cleanup jobCompare expiresAt with the current time instead of waiting for expired.