Summary
I'd like to propose a pre-flight validation layer in pyDataverse: a non-throwing API that checks dataset metadata before it is sent to a Dataverse instance and returns structured, machine-readable findings instead of failing at the first problem.
While working on #240 / #247 and #241 / #249 I noticed that several open issues share the same shape: metadata the user believed was valid, failing late, one problem at a time, as a traceback rather than as a field-level message. This proposal addresses that class of problem rather than each instance.
Layer 2 (out of scope for this issue) is written up in a separate public repo/branch, not in pyDataverse:
That document describes the multi-agent / External Tool design. This issue (#254) is only Layer 1 (deterministic pre-flight validation in pyDataverse).
Motivation
1. Metadata errors surface late, one at a time, as tracebacks.
In each case the problem is discovered late. A shared validation pass would surface these as field-level messages before the request is made, and would report all problems in one run rather than one per round trip.
2. Non-Latin metadata appears to be untested.
Dataverse installations hold multilingual metadata. Controlled-vocabulary values, author names, keywords, and descriptions in CJK and other non-Latin scripts pass through the same serialization path touched in #249, but I could not find test coverage for:
- Unicode normalization (NFC vs NFD) round-tripping
- non-ASCII values in controlled-vocabulary fields
- non-Latin author names and affiliations surviving serialize → POST → retrieve unchanged
These are possible sources of silent data corruption in a preservation system. I would like to add this coverage regardless of whether the broader agentic-deposit proposal is accepted.
3. Multi-stage deposits have no consistency check (later / optional).
Researchers depositing across stages of one study often need consistency between related datasets (authors, funding, controlled-vocabulary choices, linkage). That is valuable, but I would treat it as a follow-on after a solid single-dataset validator exists—not part of the first PR.
Proposed scope
A small, dependency-free module — deterministic and rule-based:
from pyDataverse.validation import validate_dataset_metadata
report = validate_dataset_metadata(
dataset,
blocks=("citation", "biomedical"),
server_schema=api.get_metadatablocks(), # optional; enables schema-aware checks
)
report.ok # bool
report.findings # list[Finding]
# Finding(
# level="error" | "warning",
# block="citation",
# field="authorIdentifierScheme",
# code="cv_value_not_allowed",
# message="'ORCID ' is not an allowed value for authorIdentifierScheme",
# value="ORCID ",
# allowed=["ORCID", "ISNI", "LCNA", "VIAF", "GND", "DAI", "ResearcherID", "ScopusID"],
# )
Properties:
- Collects all findings; never raises. Returns the complete set in one pass.
- Structured output. CI, notebooks, and external tools can consume it without parsing prose.
- Optionally schema-aware. Installations differ in which metadata blocks they enable; validating against the blocks a target server advertises is more useful than a hard-coded schema. Offline mode can still catch structural and Unicode issues.
- No new runtime dependency. pydantic is already in use (
Annotated[str, AfterValidator(...)]).
First PR slice (proposed)
- Public validation API for the
citation block (required / empty fields + controlled vocabulary).
- Non-Latin / Unicode round-trip test coverage described above.
- Documented example: validate → inspect findings → fix → then create / upload.
- No LLM / AI dependency.
Design principle: draft vs. published
This constrains anything built on top of the validation layer:
Automated correction acts only on draft (unpublished) versions. For published versions, tooling proposes and a human decides.
Published Dataverse versions are permanent and citable. Drafts are private and mutable, so acting on them carries no preservation risk.
Layer 1 itself only validates; it does not publish and does not auto-correct.
Non-goals
- Not changing the raise / no-raise behaviour of any existing call. This is additive.
- Not bundling any LLM or AI dependency into pyDataverse. Validation is deterministic.
- Not proposing UI. A guided deposit experience belongs in a Dataverse External Tool (
Configure type, dataset scope) built on this API — not in the client library.
- Not requiring Dataverse core changes for this issue.
Why this belongs in pyDataverse
These rules already exist in the client; they are just implicit and only observable by triggering them. Exposing them as an inspectable, non-throwing API means one implementation can serve library users, CI checks, and external tools, instead of each reimplementing the same knowledge about metadata blocks and controlled vocabularies.
Acceptance criteria (verifiable)
Related
Questions for maintainers
- Is a validation module in scope for pyDataverse, or would you rather it live in a separate package that depends on pyDataverse?
- Should offline (schema-less) validation be supported, or should validation always require a live instance's metadata blocks?
- Preference on the report data shape — dataclasses, pydantic models, or plain dicts?
- Preferred API shape — top-level function, dedicated module/class, or method on
Dataset / Dataverse?
- Should findings mirror Dataverse server validation messages where possible?
- Any in-flight model / metadata work I should align with?
I'm happy to implement this. If the direction seems reasonable, I'd start small: the citation block plus the non-Latin / Unicode round-trip tests above, as a self-contained PR — and leave multi-stage consistency / External Tool work until there's agreement on the shape.
Summary
I'd like to propose a pre-flight validation layer in pyDataverse: a non-throwing API that checks dataset metadata before it is sent to a Dataverse instance and returns structured, machine-readable findings instead of failing at the first problem.
While working on #240 / #247 and #241 / #249 I noticed that several open issues share the same shape: metadata the user believed was valid, failing late, one problem at a time, as a traceback rather than as a field-level message. This proposal addresses that class of problem rather than each instance.
Layer 2 (out of scope for this issue) is written up in a separate public repo/branch, not in pyDataverse:
maindataverse_agentic_deposit_proposal.mdThat document describes the multi-agent / External Tool design. This issue (#254) is only Layer 1 (deterministic pre-flight validation in pyDataverse).
Motivation
1. Metadata errors surface late, one at a time, as tracebacks.
TypeError: issubclass() arg 1 must be a classdeep insideMetadataBlockBase._process_field_value. The failure involved a controlled-vocabulary field (identifier_scheme/authorIdentifierScheme), but the message said nothing useful about fields, blocks, or allowed values. A pre-flight pass could surface field-level problems before the request path collapses into a traceback.astrophysicsandbiomedicalmetadata blocks.typeClassfor controlled-vocabulary fields such askindOfData.In each case the problem is discovered late. A shared validation pass would surface these as field-level messages before the request is made, and would report all problems in one run rather than one per round trip.
2. Non-Latin metadata appears to be untested.
Dataverse installations hold multilingual metadata. Controlled-vocabulary values, author names, keywords, and descriptions in CJK and other non-Latin scripts pass through the same serialization path touched in #249, but I could not find test coverage for:
These are possible sources of silent data corruption in a preservation system. I would like to add this coverage regardless of whether the broader agentic-deposit proposal is accepted.
3. Multi-stage deposits have no consistency check (later / optional).
Researchers depositing across stages of one study often need consistency between related datasets (authors, funding, controlled-vocabulary choices, linkage). That is valuable, but I would treat it as a follow-on after a solid single-dataset validator exists—not part of the first PR.
Proposed scope
A small, dependency-free module — deterministic and rule-based:
Properties:
Annotated[str, AfterValidator(...)]).First PR slice (proposed)
citationblock (required / empty fields + controlled vocabulary).Design principle: draft vs. published
This constrains anything built on top of the validation layer:
Published Dataverse versions are permanent and citable. Drafts are private and mutable, so acting on them carries no preservation risk.
Layer 1 itself only validates; it does not publish and does not auto-correct.
Non-goals
Configuretype, dataset scope) built on this API — not in the client library.Why this belongs in pyDataverse
These rules already exist in the client; they are just implicit and only observable by triggering them. Exposing them as an inspectable, non-throwing API means one implementation can serve library users, CI checks, and external tools, instead of each reimplementing the same knowledge about metadata blocks and controlled vocabularies.
Acceptance criteria (verifiable)
citationblock rules covered for required fields + controlled vocabulary.Related
astrophysics&biomedicalmetadata blockstypeClassfor controlled-vocabulary fieldsmain): https://github.com/youseihuayu-wonderful/dataverse-agentic-deposit/blob/main/dataverse_agentic_deposit_proposal.mdQuestions for maintainers
Dataset/Dataverse?I'm happy to implement this. If the direction seems reasonable, I'd start small: the
citationblock plus the non-Latin / Unicode round-trip tests above, as a self-contained PR — and leave multi-stage consistency / External Tool work until there's agreement on the shape.