| name | validate-references |
| description | Check BibTeX entries for completeness, DOI resolution, and broken links. Verify required fields per entry type (article, book, inproceedings), resolve and validate DOIs via the CrossRef API, check URL accessibility, and flag duplicate entries, missing abstracts, and inconsistent formatting. Use when preparing a manuscript bibliography for journal submission, auditing a shared .bib file before a project milestone, after merging bibliographies from multiple sources, when citations render incorrectly, or as a CI check on version-controlled .bib files.
|
| license | MIT |
| allowed-tools | Read Write Edit Bash Grep Glob |
| metadata | {"author":"Philipp Thoss","version":"1.0","domain":"citations","complexity":"intermediate","language":"R","tags":"citations, validation, doi, bibtex, quality"} |
Validate References
Check BibTeX bibliography entries for completeness, accuracy, and consistency.
This skill covers verifying required fields per entry type, resolving DOIs via
the CrossRef API, checking URL accessibility, detecting duplicate entries, and
producing a structured validation report that flags issues by severity. It
ensures that .bib files are publication-ready before rendering.
When to Use
- Preparing a manuscript bibliography for journal submission
- Auditing a shared .bib file for quality before a project milestone
- After merging bibliographies from multiple sources
- When citations render incorrectly and you need to diagnose .bib issues
- As a CI check on .bib files in version-controlled projects
Inputs
- Required: Path to a .bib file
- Optional: Validation level (
basic, standard, strict; default: standard)
- Optional: Whether to check DOI resolution online (default:
TRUE)
- Optional: Whether to check URL accessibility (default:
TRUE)
- Optional: Output report path (default: prints to console)
- Optional: CrossRef API email for polite pool (recommended for large files)
Procedure
Step 1: Install and Load Required Packages
required_packages <- c("RefManageR", "httr2", "curl")
missing <- required_packages[!vapply(required_packages, requireNamespace,
logical(1), quietly = TRUE)]
if (length(missing) > 0) install.packages(missing)
library(RefManageR)
Expected: All packages load without errors.
On failure: If httr2 is unavailable, install it with install.packages("httr2").
For systems without curl headers: sudo apt install libcurl4-openssl-dev.
Step 2: Parse and Inventory the Bibliography
bib <- RefManageR::ReadBib("references.bib", check = FALSE)
message(sprintf("Loaded %d entries from references.bib", length(bib)))
entry_types <- vapply(bib, function(x) tolower(attr(x, "bibtype")), character(1))
type_counts <- sort(table(entry_types), decreasing = TRUE)
message("Entry types:")
for (type in names(type_counts)) {
message(sprintf type type_countstype
Expected: Summary of entry types (article, book, inproceedings, etc.) and total
count matching the number of @type{ blocks in the file.
On failure: Parsing errors indicate malformed BibTeX. Check for unmatched braces,
missing commas between fields, or invalid UTF-8 characters.
Step 3: Validate Required Fields per Entry Type
required_fields <- list(
article = c("author", "title", "journal", "year"),
book = c("author", "title", "publisher", "year"),
inproceedings = c("author", "title", "booktitle", "year"),
incollection = c("author", "title", "booktitle", "publisher", "year"),
phdthesis = c("author"
mastersthesis
techreport
misc
unpublished
validate_fields bib
issues
i bib
key bibi
entry_type tolowerbibi
req required_fieldsentry_type
req
issuesissues
key key severity
message sprintf entry_type
field req
value bibifield
value trimwsvalue
issuesissues
key key severity
message sprintf field entry_type
issues
field_issues validate_fieldsbib
messagesprintf field_issues
Expected: A list of issues where required fields are missing. Zero issues for a
well-maintained bibliography.
On failure: This step runs locally and should not fail. If it does, check that the
.bib file parsed correctly in Step 2.
Step 4: Resolve and Validate DOIs
validate_dois <- function(bib, email = NULL) {
issues <- list()
headers <- list(`User-Agent` = "R-bibliography-validator/1.0")
if (!is.null(email)) {
headers[["mailto"]] <- email
}
for (i in seq_along(bib)) {
key <- names(bib)[i]
doi <- bib[[i]]$doi
if (is.null(doi) doi
issuesissues
key key severity
message
doi gsub doi
doi gsub doi ignore.case
doi trimwsdoi
tryCatch
resp httr2requestsprintf doi
httr2req_headersheaders
httr2req_timeout
httr2req_perform
httr2resp_statusresp
issuesissues
key key severity
message sprintf doi
httr2resp_statusresp
error e
issuesissues
key key severity
message sprintf doi emessage
Sys.sleep
issues
doi_issues validate_doisbib email
messagesprintf doi_issues
Expected: Each DOI resolves successfully (HTTP 200 from CrossRef). Entries without
DOIs are flagged as informational.
On failure: Network errors or rate limiting produce warnings rather than hard
failures. Set the email parameter for higher rate limits from CrossRef's polite pool.
Step 5: Check URL Accessibility
validate_urls <- function(bib) {
issues <- list()
for (i in seq_along(bib)) {
key <- names(bib)[i]
url <- bib[[i]]$url
if (is.null(url) || !nzchar(url)) next
tryCatch({
resp <- httr2::request(url) |>
httr2::req_method("HEAD") |>
httr2::req_timeout(10) |>
httr2req_erroris_error resp
httr2req_perform
status httr2resp_statusresp
status
issuesissues
key key severity
message sprintf status url
error e
issuesissues
key key severity
message sprintf url emessage
Sys.sleep
issues
url_issues validate_urlsbib
messagesprintf url_issues
Expected: All URLs return HTTP 200 (or 301/302 redirects). Broken links flagged.
On failure: Some servers block HEAD requests. Retry with GET for failed HEAD
checks. Timeout errors are common for slow academic servers.
Step 6: Detect Duplicate Entries
detect_duplicates <- function(bib) {
issues <- list()
dois <- vapply(bib, function(x) {
d <- x$doi
if (is.null(d)) NA_character_ else tolower(trimws(d))
}, character(1))
doi_table <- table(dois[!is.na(dois)])
dup_dois <- names(doi_table[doi_table > 1])
for (d in dup_dois
keys bibwhichdois d
issuesissues
key pastekeys collapse severity
message sprintf d
pastekeys collapse
titles vapplybib x
t xtitle
t tolowergsub tolowert
character
seen character
i titles
titlesi
j seen
identicaltitlesi titlesj
issuesissues
key sprintf bibj bibi
severity
message sprintf
substrbibititle
seen seen i
issues
dup_issues detect_duplicatesbib
messagesprintf dup_issues
Expected: Zero duplicates for a clean bibliography. Any detected duplicates are
flagged with the specific entry keys involved.
Step 7: Generate Validation Report
generate_report <- function(all_issues, bib, output_file = NULL) {
errors <- Filter(function(x) x$severity == "error", all_issues)
warnings <- Filter(function(x) x$severity == "warning", all_issues)
infos <- Filter(function(x) x$severity == "info", all_issues)
lines <- c(
"# Bibliography Validation Report",
"",
sprintf("**File**: references.bib"),
sprintf("**Entries**: %d", length(bib)
sprintf Sys.Date
sprintf
errors warnings infos
errors
lines lines
issue errors
lines lines sprintf issuekey issuemessage
lines lines
warnings
lines lines
issue warnings
lines lines sprintf issuekey issuemessage
lines lines
report_text pastelines collapse
output_file
writeLinesreport_text output_file
messagesprintf output_file
catreport_text
all_issues
all_issues field_issues doi_issues url_issues dup_issues
generate_reportall_issues bib output_file
Expected: A structured markdown report listing all issues grouped by severity.
Validation
Common Pitfalls
- DOI format inconsistency: DOIs may appear as
10.1234/...,
https://doi.org/10.1234/..., or doi:10.1234/.... Normalize before comparing
- CrossRef rate limiting: Unauthenticated requests are limited to ~50/second.
Always use the
email parameter to join the polite pool for higher limits
- Transient URL failures: Academic servers occasionally timeout. Retry failed
URLs once before flagging them as broken
- Entry type variations: BibLaTeX uses
@online where BibTeX uses @misc.
The validator should handle both
- False positive duplicates: Entries like "Introduction" or "Methods" as titles
trigger fuzzy matching. Review flagged duplicates manually
- Missing DOIs for older works: Pre-2000 publications often lack DOIs. Flag as
informational, not as errors
Related Skills
manage-bibliography - fix issues found by this validator (dedup, add fields)
format-citations - format validated entries into styled citations
../reporting/format-apa-report - APA reports require complete, validated references
../r-packages/write-vignette - vignettes with citations need valid .bib entries