Skip to content

feat!: keep error causes in domain_linkage, linked_vp, methods and keys - #128

Merged
daniel-mader merged 5 commits into
mainfrom
claude/inspiring-archimedes-echxvg-more-error-sources
Oct 26, 2026
Merged

daniel-mader merged 5 commits into
mainfrom
claude/inspiring-archimedes-echxvg-more-error-sources

Conversation

@daniel-mader

@daniel-mader daniel-mader commented Oct 26, 2026 •

Copy link
Copy Markdown
Contributor

Closes #124.

#118 made the vc, sd_jwt_vc and jose errors keep their cause as source(), and docs/DESIGN.md states the rule. These modules still formatted a wrapped error into a String, so source() was None and the cause survived only as text. Each one now keeps it.

What changes

Error Variant Source
DomainLinkageError InvalidOrigin(String) → { origin, reason, source } url::ParseError (not a URL) or the URI error (not RFC 3986); none for "no host" and "not a domain origin"
DomainLinkageError InvalidService + source the origin's InvalidOrigin, or the URI or URL error
LinkedVpError InvalidService + source the URI or URL error
LinkedVpError InvalidBody + source the Utf8Error of a binary body, or the JwsError
ProduceError UnsupportedKey + source did:jwk: the DidError or ResolutionError; did:key: the DidKeyError; did:web: the DidError of a bad kid
ProduceError InvalidParams(String) → { reason, source } did:web: the WebError of a URL that isn't a did:web location
KeyStoreError InvalidKey + source PKCS #11: the JwkError of a public key that doesn't convert; software: the p256/k256 error of an out-of-range scalar, or the JwkError of a missing thumbprint
KeyStoreError InvalidKeySet + source the serde_json::Error of a file that isn't JSON, or the key error of one of its keys
  • Boxed sources: every source is a Box<dyn Error + Send + Sync>. Most variants mix several cause types, and the url, p256 and k256 errors would otherwise become part of this crate's semver surface. That follows feat!: keep error causes as source() #118's rule.
  • did:key: isn't in the issue's table, but it formatted its DidKeyError the same way, so I changed it too.
  • Messages: the private cause() helper ends every message with its cause's, as feat!: keep error causes as source() #118 does, so no message loses information. A few now name the reason before the cause, for example invalid origin "": not a URL: empty host and key 0 cannot be used: it has no did:key: ….
  • Unchanged: strings that only describe the input stay as they are, with source: None: "no host", "serviceEndpoint is a map, not URLs", "use is not sig", and so on.
  • feat(domain-linkage): read and edit configuration entries unverified #127's tests: they matched InvalidOrigin(_); they now match InvalidOrigin { .. }.

For implementors outside the crate

DidProducer and KeyStore are extension points. UniCore's own key store and DID methods build ProduceError::UnsupportedKey/InvalidParams and KeyStoreError::InvalidKey themselves, so adding a field breaks them. Three constructors cover the case without a cause:

  • ProduceError::unsupported_key(index, reason);
  • ProduceError::invalid_params(reason);
  • KeyStoreError::invalid_key(key, reason).

The crate's own sites without a cause use them. With a cause, implementors write the struct variant. tests/resolver.rs's custom producer shows both, and DESIGN.md notes the convention.

Smaller changes

  • LinkedVpError::InvalidBody: reason is now &'static str. With a Url, a String and the source, the variant pushed LinkedVpError over clippy's large-error limit. Both of its reasons are fixed text anyway.
  • Software store: an Unusable struct (reason + source) replaces the String the private LocalKey::from_jwk and Entry::from_jwk returned. for_key and for_key_set turn it into InvalidKey or InvalidKeySet.

Breaking changes

The title is feat!, so the SemVer job skips this PR. cargo semver-checks locally reports exactly these:

  • Changed shape: DomainLinkageError::InvalidOrigin and ProduceError::InvalidParams went from tuple to struct variants.
  • Added field: source on DomainLinkageError::InvalidService, LinkedVpError::InvalidService and InvalidBody, ProduceError::UnsupportedKey, and KeyStoreError::InvalidKey and InvalidKeySet.
  • Changed type: LinkedVpError::InvalidBody::reason is now &'static str.

None of these types derived Clone or Eq before, so no derives are lost.

Tests

For each changed variant, a test checks source() by downcasting, and that a variant built without a cause has none:

  • domain_linkage:

    • normalize_origin("mailto:…") has no source, with the exact new message;
    • normalize_origin("") has a url::ParseError;
    • normalize_origin("`example.com") has a DidError (URI);
    • service with that origin has an InvalidOrigin.
  • linked_vp:

    • a URL that isn't a URI has a DidError;
    • fetching a binary body gives a Utf8Error;
    • fetching text or an empty body gives a JwsError.
  • Methods:

    • did:key with an over-long key has a DidKeyError, with the message's new reason;
    • did:jwk with a key JCS can't serialize has a DidError;
    • did:web with an http URL has a WebError, and a bad kid a DidError.
  • resolver: a custom producer returns both InvalidParams forms; the constructors' messages, and that they have no source.

  • keys:

    • importing a P-256 key whose d is zero has a p256::ecdsa::Error;
    • a key marked use: enc has no source;
    • invalid_key's message;
    • a key set file that isn't JSON has a serde_json::Error.

    Two tests that matched on reason text now match on Display, which includes the cause.

Checks

All with rustc/clippy 1.99.1, on the branch with main (#125, #126, #127, #133) merged in:

  • fmt;
  • clippy --all-features --all-targets -D warnings;
  • cargo hack clippy --each-feature -D warnings;
  • docs with -D warnings;
  • cargo test --all-features;
  • the --no-default-features --features did-key,software build;
  • cargo publish --dry-run;
  • cargo semver-checks as above.

.cargo/mutants.toml pins no lines in the files this touches.


Generated by Claude Code

Summary by CodeRabbit

  • Bug Fixes

    • Error messages now provide clearer context for invalid origins, services, keys, and response bodies, including the reason for failure.
    • Underlying parsing and validation errors are retained, making more failure details available when troubleshooting.
  • Documentation

    • Clarified how error causes appear in messages and how to create errors without an underlying cause.

The controller of an origin maintains the DID configuration it
publishes: it renews credentials before they expire and unlinks an
origin by dropping its credentials. Both need each entry's DID, origin
and expiry, read back from credentials it signed itself.

`DomainLinkageConfiguration::entries` reads each JWT entry without
verifying it, as an `UnverifiedLinkage` (DID, normalized origin, issuer,
`nbf`, `exp`) or the reason it does not read. `earliest_expiry` says
when renewal is due, and `remove_origin` drops the entries of an origin,
compared normalized, keeping entries that do not read.
`UnverifiedLinkage::from_jwt` reads one credential.

The subject DID and origin are read by the same helpers
`DomainLinkageValidator` uses, so both agree on what an entry says.

Closes #123.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PB1rHNJ3YpjogEUJQYccc2
…and `keys`

#118 made the `vc`, `sd_jwt_vc` and `jose` errors keep their cause as
`source()`. These modules still formatted a wrapped error into a
`String`; each now keeps it, boxed since the causes are `url`, `p256`
and `k256` errors or a mix of this crate's:

- `DomainLinkageError::InvalidOrigin` becomes `{ origin, reason, source }`
  and `InvalidService` gains `source` (the `InvalidOrigin`, URI or URL
  error);
- `LinkedVpError::InvalidService` gains `source`, and `InvalidBody` gains
  `source` (the UTF-8 error or `JwsError`) with a `&'static str` reason
  that keeps the enum under clippy's large-error limit;
- `ProduceError::UnsupportedKey` gains `source` and `InvalidParams`
  becomes `{ reason, source }`; `did:jwk`, `did:key` and `did:web` set
  it;
- `KeyStoreError::InvalidKey` and `InvalidKeySet` gain `source`; the
  PKCS #11 store keeps the `JwkError` of a public key, the software
  store the key errors and the JSON error of its file.

`ProduceError::unsupported_key`, `ProduceError::invalid_params` and
`KeyStoreError::invalid_key` build the variants without a cause, for
`DidProducer` and `KeyStore` implementations outside the crate. Every
message still ends with its cause's.

Closes #124.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PB1rHNJ3YpjogEUJQYccc2
@coderabbitai

coderabbitai Bot commented Oct 26, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 50 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

Learn how review limits work.

Review configuration:

Run configuration
Commits

Reviewing files that changed from the base of the PR and between b0c10c3 and fca2047.

Files selected for processing: 20
  • docs/DESIGN.md
  • src/domain_linkage/mod.rs
  • src/keys/error.rs
  • src/keys/local.rs
  • src/keys/pkcs11.rs
  • src/keys/signer.rs
  • src/keys/software.rs
  • src/linked_vp/mod.rs
  • src/methods/jwk.rs
  • src/methods/key.rs
  • src/methods/web/mod.rs
  • src/resolver/producer.rs
  • tests/domain_linkage.rs
  • tests/keys.rs
  • tests/keys_pkcs11.rs
  • tests/linked_vp.rs
  • tests/methods_jwk.rs
  • tests/methods_key.rs
  • tests/methods_web.rs
  • tests/resolver.rs

No actionable comments were generated in the recent review.

Recent review info
Run configuration
Commits

Reviewing files that changed from the base of the PR and between dd90ca7 and b0c10c3.

Files selected for processing: 20
  • docs/DESIGN.md
  • src/domain_linkage/mod.rs
  • src/keys/error.rs
  • src/keys/local.rs
  • src/keys/pkcs11.rs
  • src/keys/signer.rs
  • src/keys/software.rs
  • src/linked_vp/mod.rs
  • src/methods/jwk.rs
  • src/methods/key.rs
  • src/methods/web/mod.rs
  • src/resolver/producer.rs
  • tests/domain_linkage.rs
  • tests/keys.rs
  • tests/keys_pkcs11.rs
  • tests/linked_vp.rs
  • tests/methods_jwk.rs
  • tests/methods_key.rs
  • tests/methods_web.rs
  • tests/resolver.rs

Included review availability: This review used your included allowance. Your plan provides up to 1 included review per hour; 0 remain after this review.

Walkthrough

Error variants across domain linkage, linked VP, key stores, and DID production now retain optional underlying errors. Display messages include causes when present. Tests verify source types and cause-free cases, and the design documentation describes the error behavior.

Changes: Error cause preservation

Layer / File(s) Summary
Domain-linkage and linked-VP errors
src/domain_linkage/mod.rs, src/linked_vp/mod.rs, tests/domain_linkage.rs, tests/linked_vp.rs
These errors retain optional sources for parsing and validation failures. Tests check structured error details and source types.
Key-store validation errors
src/keys/error.rs, src/keys/local.rs, src/keys/pkcs11.rs, src/keys/signer.rs, src/keys/software.rs, tests/keys.rs, tests/keys_pkcs11.rs
Key-store errors retain causes from key conversion, thumbprint, and JSON parsing. A constructor creates cause-free invalid-key errors. Tests check source types and cause-free cases.
DID producer errors
src/resolver/producer.rs, src/methods/jwk.rs, src/methods/key.rs, src/methods/web/mod.rs, tests/methods_jwk.rs, tests/methods_key.rs, tests/methods_web.rs, tests/resolver.rs, docs/DESIGN.md
Producer errors add optional sources and cause-free constructors. DID method adapters and resolver tests preserve and inspect causes. The design documentation describes the error behavior.

Priority: Normal

Estimated code review effort: 3 (Moderate) | ~25 minutes

Change: Bug fix

Possibly related PRs

Merge Risk: Minimal · previous review b0c10

No actionable merge-blocking issue is established; proceed with the normal test checks before merging.

Pre-merge checks: Passed 4 · Failed 1

Failed Failed checks: 1 warning

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 60.71% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 56 functions across 19 files. (1 skipped:… Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed Passed checks: 4 passed
Check name Status Explanation
Description Check Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check Passed The title clearly summarizes the primary change: preserving error causes across domain_linkage, linked_vp, methods, and keys. It is concise and specific.
Linked Issues check Passed Issue #124 requires changed errors in domain_linkage, linked_vp, methods, and keys to retain causes through source(), preserve reason context, document source behavior, and add tests. The PR adds opti…
Out of Scope Changes check Passed The changes stay within issue #124. Source-preserving changes cover the listed modules and related did:key handling uses the same error-cause rule. The added constructors, documentation, implementatio…
Full details: Linked Issues check

Explanation

Issue #124 requires changed errors in domain_linkage, linked_vp, methods, and keys to retain causes through source(), preserve reason context, document source behavior, and add tests. The PR adds optional boxed sources or existing typed sources across the listed variants, keeps cause text in Display messages, updates docs/DESIGN.md, and adds downcast and no-source tests. The public API changes and constructors cover the stated breaking-change requirements.

Full details: Out of Scope Changes check

Explanation

The changes stay within issue #124. Source-preserving changes cover the listed modules and related did:key handling uses the same error-cause rule. The added constructors, documentation, implementation updates, and tests support the error API change. No unrelated behavior or feature is demonstrated.

Full details: Docstring Coverage

Explanation

Docstring coverage is 60.71% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 56 functions across 19 files. (1 skipped: 1 unsupported.)

Resolution

Write docstrings for the functions missing them to satisfy the coverage threshold.

Finishing Touches: 💡 1
📝 Generate docstrings: 💡
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@codecov

codecov Bot commented Oct 26, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 81.77083% with 35 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/keys/pkcs11.rs 42.10% 5 Missing and 6 partials ⚠️
src/domain_linkage/mod.rs 82.00% 6 Missing and 3 partials ⚠️
src/keys/local.rs 84.61% 6 Missing and 2 partials ⚠️
src/keys/signer.rs 63.63% 3 Missing and 1 partial ⚠️
src/methods/jwk.rs 50.00% 3 Missing ⚠️

📢 Thoughts on this report? Let us know!

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

…tries' into claude/inspiring-archimedes-echxvg-more-error-sources

Copy link
Copy Markdown
Contributor Author

@coderabbitai review


Generated by Claude Code

@coderabbitai

coderabbitai Bot commented Oct 26, 2026 •

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Base automatically changed from claude/inspiring-archimedes-echxvg-did-configuration-entries to main October 26, 2026 22:32
…imedes-echxvg-more-error-sources

# Conflicts:
#	tests/domain_linkage.rs
@daniel-mader
daniel-mader merged commit f093804 into main Oct 26, 2026
33 checks passed
@daniel-mader
daniel-mader deleted the claude/inspiring-archimedes-echxvg-more-error-sources branch October 26, 2026 22:40
This was referenced Oct 26, 2026
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.

Keep error causes in domain_linkage, linked_vp, methods and keys

2 participants