Files
magnus919_agent-skills/anydoc/references/errors.md
T
Magnus HedemarkGitHubfactory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
f37dc73829 feat(skill): add anydoc — office documents to GitHub-Flavored Markdown (#295)
* feat(skill): add anydoc core content and references

Add the anydoc skill content tree: SKILL.md (progressive-disclosure index
with frontmatter per ALLOWED_FIELDS), human-facing README, the five reference
files (formats, cli-reference, errors, workflows, sources), 24 committed
fixtures (valid + error cases), and a fixture-grounded eval manifest with 8
cases. Every documented behavior, exit code, and error message was verified
against the real pinned CLI (npx -y @firecrawl/anydoc@0.1.6); verbatim --help
and error transcripts are reproduced character-for-character.

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>

* feat(skill): add anydoc wrapper script and unit tests

Implements scripts/anydoc, a stdlib-only Python wrapper around the pinned
@firecrawl/anydoc@0.1.6 CLI: convert/batch/info subcommands, global
--json/--dry-run, input and output pre-validation, friendly hints for the
no-OCR/encrypted/malformed/unsupported error classes, Node >= 20 and npx
availability checks, deterministic batch output naming with documented
duplicate/collision behavior, and exit codes 0/1/2. Adds offline unittest
suite (46 tests, real-CLI tests skip when npx is unavailable) and keeps the
wrapper contract documented in cli-reference.md and errors.md.

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>

* feat(skill): ratchet anydoc evals to 14 grounded cases

Verify the pre-authored 8-case manifest and extend it with six
high-signal cases (PDF lower-fidelity pipeline, legacy .ppt table
flattening, ODP same-serializer, RTF, EPUB, CSV header promotion),
each grounded in real pinned-CLI runs against the committed fixtures.

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>

* feat(skill): integrate anydoc into repo catalog and artifacts

Add the sorted anydoc catalog entry to README.md (between agent-skills
and api-design-and-evolution), regenerate the tracked catalog artifacts
(.claude-plugin/marketplace.json, .codex-plugin/plugin.json,
.agents/plugins/marketplace.json, llms.txt) with the ruby generators,
and add a routing note to documents/SKILL.md pointing office-document
to-markdown conversion at the anydoc skill.

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>

* fix(skill): polish anydoc wrapper timeout, JSON shape, and docs

- run_cli raises CliTimeoutError on the 120s timeout; convert/batch with
  --json now emit one parseable JSON error envelope (error_class "timeout")
  on stdout before exiting, so --json always yields exactly one JSON doc
- batch JSON failure entries (pre-validation and CLI) now carry error_class
  ("io" for missing/dir inputs, mapped classes for CLI failures), so all
  batch failure entries share the same shape
- build_cli_command places -o/-f before the -- separator for dash-leading
  filenames, so `convert -f csv -- -weird` converts instead of misparsing
  ("unexpected second input"); absolute-path inputs unchanged
- workflows.md vault-ingestion recipe globs notes/* instead of docs/* and
  warns to run from a temp/vault dir, never touching repo-root docs/
- unit tests: +6 (timeout envelope x4, batch error_class shape,
  dash-leading filename); suite grows 46 -> 52

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>

---------

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
2026-08-06 20:11:19 -04:00

223 lines
10 KiB
Markdown

# Errors, exit codes, and troubleshooting
Every message below is a **verbatim real stderr capture** from the pinned CLI
(`@firecrawl/anydoc@0.1.6`) run against the committed fixtures in `fixtures/`
(and, for resource limits, generated oversized archives). The CLI prints
exactly one line to stderr, prefixed `anydoc: `, and never prompts.
## Exit codes
| Code | Meaning | Triggers |
| --- | --- | --- |
| `0` | Success | Normal conversion; `--help`/`--version`; also on **EPIPE** when the downstream pipe closes early (`anydoc big.xlsx \| head`). |
| `1` | The document could not be read or converted | Any conversion or IO failure below: missing file, unsupported input, scanned/image-only PDF, malformed archive, encrypted document, resource limit, `-o` pointing at a directory. |
| `2` | Usage error | Unknown option, missing input, invalid `--format`, more than one input, an option missing its value, stdin is a terminal. |
## Conversion / IO failures (exit code 1)
### io — the file could not be read
```
anydoc: io error: No such file or directory (os error 2)
```
This is the missing-file case (`to_markdown` path only; stdin and byte APIs
have no io error).
### unsupported — unknown format or unconvertible content
Unknown content **and** unknown extension (the extension is echoed as given):
```
anydoc: unsupported input: unrecognized file content and extension: unsupported.xyz
```
Verified against `fixtures/unsupported.xyz` (run from the fixture directory,
the tail is `unsupported.xyz`; when you pass a longer path, that path is echoed).
Recognized format but unconvertible content — a **scanned or image-only PDF**
(the CLI detects the page count and that it looks scanned):
```
anydoc: unsupported input: PDF has no extractable text (Scanned, 1 pages): OCR is required
```
Verified against `fixtures/scanned-image-only.pdf`: exit 1, empty stdout.
### unsupported — stdin without a format
CSV has no content signature and stdin has no extension, so CSV piped to `-`
without `--format csv` fails:
```
anydoc: unsupported input: unrecognized file content: name the format explicitly
```
Fix: add `--format csv` (e.g. `cat data.csv | npx -y @firecrawl/anydoc@0.1.6 - --format csv`).
### malformed — structurally unusable archive
An empty (0-byte) `.docx` and a truncated `.docx` both produce:
```
anydoc: malformed document: not a readable zip archive: invalid Zip archive: Could not find EOCD
```
Verified against `fixtures/empty--errors.docx`. Any other structurally broken
package surfaces the same class.
### encrypted — password-protected document
```
anydoc: document is encrypted
```
Verified against `fixtures/encrypted--errors.odt`. There is **no password or
decryption option** anywhere in the CLI or library — the only fix is an
unencrypted copy of the file.
### resourceLimit — fixed safety limits (decompression / nesting / node count)
Zip-bomb style DOCX (giant `word/document.xml`):
```
anydoc: resource limit exceeded (max_entry_bytes): word/document.xml declares 201326759 decompressed bytes
```
Image-bomb style DOCX (giant `word/media/image1.png`):
```
anydoc: resource limit exceeded (max_entry_bytes): word/media/image1.png declares 201326592 decompressed bytes
```
The **character-exact prefix** is:
```
anydoc: resource limit exceeded (max_entry_bytes):
```
with a tail naming the offending entry and the declared decompressed size —
the tail varies by entry, so match on the prefix. Verified also with a
generated 250 MB-entry zip (tail: `word/document.xml declares 250000000
decompressed bytes`). anydoc rejects zip/image bombs via `max_entry_bytes`;
conversion is **not streaming**, and the whole entry is checked before use.
### output-is-directory (EISDIR)
`-o` pointing at an existing directory fails with exit 1:
```
anydoc: EISDIR: illegal operation on a directory, open '<path>'
```
Verified: `npx -y @firecrawl/anydoc@0.1.6 report.rtf -o /tmp` prints
`anydoc: EISDIR: illegal operation on a directory, open '/tmp'` and exits 1.
Fix: pass a file path (or a path in a directory that exists); anydoc **does
not create directories**.
## Usage errors (exit code 2)
All verified verbatim:
```
anydoc: missing input: pass a document path, or - for stdin (see anydoc --help)
anydoc: unknown option '--bogus' (see anydoc --help)
anydoc: invalid format 'bogus'; expected one of: doc, docx, odt, pdf, ppt, pptx, rtf, epub, xlsx, ods, odp, csv
anydoc: one document per invocation: unexpected second input '<path>'
anydoc: stdin is a terminal; pipe or redirect a document into anydoc -
anydoc: -o requires a value (pattern: `<option> requires a value`)
```
Notes:
- `unknown option '--bogus'` echoes the offending token; `one document per
invocation` echoes the second input path as given; the `-o requires a value`
pattern applies to `-f` too (`-f requires a value`).
- Usage errors never touch the filesystem and produce no markdown.
## The no-OCR caveat (read before converting PDFs)
- anydoc converts **text-based PDFs locally** via `pdf-inspector`; there is no
OCR service anywhere in the pipeline.
- **Scanned / image-only PDFs fail as `unsupported`** with the exact message
above (`... OCR is required`). The library's stance: "Scanned and image-only
PDFs need OCR, which anydoc does not do."
- **Route, don't retry.** When this message fires: report the exact error,
state that OCR is required, and direct the user to OCR tooling or the hosted
Firecrawl Parse API. Do **not** retry the same file locally, do **not**
claim anydoc can OCR, and do **not** fabricate the document's content.
- There is no password option, no OCR option, and no retry-until-success
behavior to enable.
## Troubleshooting recipes
| Symptom | Message to match | Fix |
| --- | --- | --- |
| File not found | `io error: No such file or directory` | Check the path; anydoc does not glob or resolve relative to the skill. |
| Unknown file type | `unsupported input: unrecognized file content and extension: <path>` | Confirm the extension is one of the 21 supported; or force it with `--format <name>`. |
| Scanned PDF | `PDF has no extractable text (Scanned, N pages): OCR is required` | Route to OCR tooling / Firecrawl Parse. Never retry locally. |
| Encrypted file | `document is encrypted` | Ask for an unencrypted copy; there is no password option. |
| Empty/truncated archive | `malformed document: not a readable zip archive` | Re-download or re-export the file. Note: some damaged files still convert partially (see below). |
| Huge or malicious archive | `resource limit exceeded (max_entry_bytes):` | anydoc rejected the entry by design; do not bypass. For genuinely large real documents, use `-o out.md`. |
| `-o` "failed" | `EISDIR: illegal operation on a directory, open '<path>'` | Point `-o` at a file path inside an existing directory. |
| CSV from stdin failed | `unsupported input: unrecognized file content: name the format explicitly` | Add `--format csv`. |
| Command rejected | any `anydoc: ...` exit-2 message | Re-read the usage: one input only, valid `--format`, options before/after correctly placed. |
## Graceful recovery — exit 0 is not byte-perfect fidelity
The library skips broken parts rather than failing whenever some meaningful
Markdown is still producible. The upstream test suite ships `*--recovers.*`
and `*--skips.*` fixtures (e.g. `mismatched--recovers.docx`,
`unbalanced--recovers.rtf`, `corrupt-styles--skips.docx`): structurally damaged
documents often convert with exit 0, dropping only the broken part. So a
conversion that exits 0 can still be incomplete — run the output-verification
steps in [workflows.md](workflows.md) and [SKILL.md](../SKILL.md) when fidelity
matters.
## Wrapper (`scripts/anydoc`) error behavior
The wrapper mirrors the CLI's contract and adds pre-validation and hints:
- **Pre-validation errors (exit 1)**: a missing input path, a directory-as-
input, or an `-o` path that is an existing directory is caught before the
CLI runs — stderr names the path and the problem (e.g.
`anydoc: input file not found: <path>`,
`anydoc: input path is a directory, not a file: <path>`,
`anydoc: output path is a directory: <path> (pass a file path; -o does not
create directories)`), with no traceback and no prompt.
- **Usage errors (exit 2)**: an unknown option, a missing input, or an invalid
`-f` value exits 2 with a usage message on stderr before any CLI invocation.
The accepted `-f` names are the 12 canonical formats plus the 9 aliases
(`anydoc: invalid format 'bogus'; expected one of: ...`).
- **Friendly hints (exit 1)**: known failure classes get a hint plus a next
step on stderr — no-OCR (`scanned-image-only.pdf` → "anydoc does not
perform OCR. Route the file to OCR tooling or the hosted Firecrawl Parse
API; do not retry it locally."), encrypted ("the document is encrypted or
password-protected — supply an unencrypted copy"), malformed ("the document
is malformed or corrupt (not a readable zip archive) — re-export or
re-download the file and retry"), unsupported ("unsupported or unrecognized
file type — check that the extension is one of the supported formats, or
force it with `-f <format>`"). The raw CLI error line is always printed
first, verbatim.
- **Node check (exit 1)**: if `node` is missing or older than v20, stderr
states that Node.js >= 20 is required (`anydoc: Node.js >= 20 is required
but `node` was not found on PATH ...` / `anydoc: Node.js version v18.20.0 is
too old; anydoc requires Node.js >= 20 ...`), before any CLI invocation.
- **npx missing (exit 1)**: stderr names `npx` and the pinned package
(`@firecrawl/anydoc@0.1.6`): `anydoc: `npx` was not found on PATH —
conversion runs via `npx -y @firecrawl/anydoc@0.1.6`. Install Node.js >= 20
(which ships npx), or install the CLI permanently with `npm install -g
@firecrawl/anydoc`.`.
- **Batch exit policy**: `batch` exits 1 when any input failed; per-file
status lines (`ok <file> -> <out.md>` / `FAIL <file>`) and a summary
(`summary: N total, S succeeded, F failed`) print to stdout, failure detail
to stderr.
- **`--json`**: exactly one JSON document on stdout in success and failure
(result, exit code, output path, optional embedded markdown for `convert`;
per-file status plus summary for `batch`); human diagnostics stay on stderr.
- **`--dry-run`**: prints the plan (the exact `npx` command line and output
paths) and executes nothing — no CLI spawn, no output files, no directory
creation.
- The wrapper always passes `-y` to npx and never prompts, even on a cold
cache.