Skip to content

feat(errors): structured Q-5-6/Q-5-7 diagnostics for resource-copy failures (bd-bxrkxblx)#318

Merged
cscheid merged 1 commit into
mainfrom
feature/bd-bxrkxblx-resource-copy-diagnostic
Jun 19, 2026
Merged

feat(errors): structured Q-5-6/Q-5-7 diagnostics for resource-copy failures (bd-bxrkxblx)#318
cscheid merged 1 commit into
mainfrom
feature/bd-bxrkxblx-resource-copy-diagnostic

Conversation

@cscheid

@cscheid cscheid commented Jun 19, 2026

Copy link
Copy Markdown
Member

Summary

Referencing a resource (e.g. an image) whose source is missing previously produced a plain, code-less error from OutputSinkError::Copy, flattened to QuartoError::other(String) and printed via the legacy single-line path:

error: <doc>: failed to copy <src> -> <dest>: No such file or directory (os error 2)

No error code, no explanation, no source span. This PR routes it (and its sibling I/O faults) through quarto-error-reporting as structured diagnostics, splitting the two failure modes:

  • Q-5-6 "Referenced resource not found" — a warning, located on the offending ![](…) reference. Detected via runtime.is_file(src) before the copy is attempted; the render continues without the file (matching Quarto 1's tolerant behavior).
  • Q-5-7 "Resource copy failed" — a hard error for a genuine flush-time fault (permission denied, disk full). Span-less; the underlying io::ErrorKind is surfaced as advisory detail. Reaches the pretty reporting path via QuartoError::Parse.

Implementation

  • ResourceCopyIntent { src, dest, origin: SourceInfo } replaces the bare (PathBuf, PathBuf) copy-intent tuple, carrying the reference's span (img.target_source.url, falling back to img.source_info). Threaded through the render context, stage context, collector, and both drain sites.
  • New resource_copy_diagnostics module with the two converters plus shared enqueue_resource_copies (existence-check + enqueue) and copy_failure_error helpers, mirroring the existing resource_error_to_parse_error precedent. Both the native (render_document_to_file) and WASM/preview (pass2_renderer::flush_resource_copies) drains use them.
  • Only OutputSinkError::Copy routes to Q-5-7; HTML-write / dir-create faults keep their existing path.

Tests

  • Catalog presence test for Q-5-6/Q-5-7.
  • Converter unit tests: missing → Q-5-6 located warning; permission-denied → Q-5-7 with advisory; disk-full surfaces the raw OS message.
  • Integration regression tests (single-doc + website) asserting a missing image yields a Q-5-6 warning and the render still succeeds.

Docs

docs/errors/project/Q-5-6.qmd, docs/errors/project/Q-5-7.qmd.

Verification

  • cargo nextest run --workspace — 10,284 passed.
  • cargo xtask verify (Rust + WASM + hub-client builds and tests) — all steps passed.
  • End-to-end through the binary: q2 render of a doc with a missing image prints the pretty [Q-5-6] diagnostic underlining the reference, exit 0, HTML produced. q2 render docs/ turns the former hard copy error into Q-5-6 warnings (168/168 files rendered).

The one remaining error in a full docs/ render (duplicate crossref identifier fig-charts) is a pre-existing, unrelated docs-content bug, tracked separately in bd-rr6qzcvu.

Plan: claude-notes/plans/2026-06-19-resource-copy-error-diagnostic.md

🤖 Generated with Claude Code

…ilures (bd-bxrkxblx)

Referencing a resource (e.g. an image) whose source was missing produced a
plain, code-less `error: <doc>: failed to copy <src> -> <dest>: No such file
or directory (os error 2)` from `OutputSinkError::Copy`, flattened to
`QuartoError::other(String)` and printed via the legacy single-line path. No
error code, no explanation, no source span.

Split the two failure modes and give each a catalog code + docs page:

- Q-5-6 "Referenced resource not found" — a *warning*, located at the
  offending reference. Detected at the drain site via `runtime.is_file(src)`
  *before* the copy is attempted; the render continues without the file
  (matching Quarto 1's tolerant behavior). Underlines the exact `![](...)`.
- Q-5-7 "Resource copy failed" — a hard *error* for a genuine flush-time copy
  fault (permission denied, disk full). Span-less; the underlying
  `io::ErrorKind` is surfaced as advisory detail. Routes through the pretty
  reporting path via `QuartoError::Parse`.

Implementation:
- `ResourceCopyIntent { src, dest, origin: SourceInfo }` replaces the bare
  `(PathBuf, PathBuf)` copy-intent tuple, carrying the reference's span
  (`img.target_source.url`, falling back to `img.source_info`). Threaded
  through the render context, stage context, collector, and both drain sites.
- New `resource_copy_diagnostics` module with the two converters plus shared
  `enqueue_resource_copies` (existence-check + enqueue) and `copy_failure_error`
  helpers, mirroring `resource_error_to_parse_error`. Both native
  (`render_document_to_file`) and WASM/preview
  (`pass2_renderer::flush_resource_copies`) drains use them. Only
  `OutputSinkError::Copy` routes to Q-5-7; HTML-write / dir-create faults keep
  their existing path.

Tests:
- Catalog presence test for Q-5-6/Q-5-7.
- Converter unit tests (missing→Q-5-6 located warning; permission-denied→Q-5-7
  with advisory; disk-full surfaces the raw OS message).
- Integration regression tests (single-doc + website) asserting the missing
  image yields a Q-5-6 warning and the render still succeeds.

Docs: `docs/errors/project/Q-5-6.qmd`, `Q-5-7.qmd`.

Verified end-to-end: `q2 render` of a doc with a missing image prints the
pretty [Q-5-6] diagnostic underlining the reference, exit 0, HTML produced;
`q2 render docs/` turns the former hard copy error into Q-5-6 warnings
(166/166 files rendered). Full `cargo nextest run --workspace` and
`cargo xtask verify` green.

Plan: claude-notes/plans/2026-06-19-resource-copy-error-diagnostic.md

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@cscheid cscheid merged commit 5f10bb6 into main Jun 19, 2026
5 checks passed
@cscheid cscheid deleted the feature/bd-bxrkxblx-resource-copy-diagnostic branch June 19, 2026 16:22
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.

1 participant