Spreadsheet rendering SDK · self-hosted CLI, Docker, and .NET no Office · no LibreOffice · BUSL-1.1

The compatibility report, and why exit 2 is a feature

· Purpose Source Association · all posts

Every spreadsheet-to-PDF converter has limits. Most of them keep the list private, and you find out which limit you hit when a client asks why a chart is missing from page four.

Rendlio Sheets’ answer is a file. Every conversion — and every diagnose run — emits one JSON report that answers three questions: what was in this workbook, what did Rendlio Sheets do with each thing, and what should you check? It exists to enforce a single engine-wide rule: nothing is ever silently approximated. Every deviation from faithful rendering is a report entry with a location.

This post is the short tour. The normative version is the compatibility report schema.

It is an API, not a log file

The report is a public, versioned JSON document with a published JSON Schema, not human-readable output that happens to be parseable. Three consequences that matter in practice:

  • The envelope is strict. additionalProperties: false at every level, so a report never carries an undocumented field you might come to depend on.
  • It is additively versioned. schemaVersion is a semver-like 1.x; new optional fields bump the minor, anything else bumps the major and gets a new schema URL. A validator pinned to v1 keeps working across engine updates.
  • Output ordering is deterministic. Identical runs produce byte-identical reports, so a report is a legitimate thing to check into a repository and diff.

You get it with --report <path>. Programmatic hosts should always set it; --report - gives you a summary on stderr instead, which is the interactive-use shape.

Four statuses, and the one that matters

The heart of the report is features[]. Each entry names a stable feature id, how many occurrences it covers, up to twenty locations, and one of exactly four statuses:

StatusWhat it means
renderedFaithful, per the specification rule for that feature.
approximatedRendered, but a documented deviation applies — detail says which one.
unsupportedPresent in the file and not rendered; the space it would occupy follows the specified rule for that feature.
skippedPresent but intentionally outside conversion semantics — not a fidelity gap. A VBA project is skipped, never executed.

There is one reporting asymmetry worth internalising, because it changes how an empty report reads: rendered entries are recorded only in diagnose mode. A convert-mode report lists deviations only, which keeps it small. So in a convert report, an empty features array does not mean the report failed to populate — it means nothing deviated.

The status that deserves your attention is approximated. unsupported is loud by construction; approximated is the case where you did get a rendered document and something in it followed a documented rule rather than the workbook. The detail string states which rule applied.

The exit code is that verdict as a number

You do not have to parse anything to gate on the report. One ladder derives both the report’s result.status and the process exit code, and the two are always in agreement:

if internal bug → error / 5
elif resource limit hit → resource-limit / 4 (outputs deleted)
elif input invalid (not xlsx, encrypted) → invalid-input / 3
elif any feature status == unsupported → unsupported-content / 2
elif any warning OR any approximated → warnings / 1
else → success / 0
CodeMeaning
0clean
1rendered with warnings
2unsupported content present
3invalid input
4resource limit hit
5internal error

Why exit 2 is a feature

Exit code 2 is the one that surprises people, so it is worth being blunt about what it is not: it is not a failure. The document was written and it is complete. What the 2 tells you is that the workbook contained something Rendlio Sheets does not yet render, and that features[] names it, with a location.

Read it the other way around. A converter that returned 0 there would have made the same decision — it cannot render that chart type either — and simply not mentioned it. You would have shipped the same PDF, minus the knowledge that a chart is missing from it. The 2 is not Rendlio Sheets doing less; it is Rendlio Sheets telling you what every converter in this category is already doing quietly.

That is why 2 is a review this signal rather than a broken build. Most pipelines treat <= 1 as pass and 2 as review:

Terminal window
rendlio sheets convert workbook.xlsx -o out.pdf --format pdfa --report report.json
code=$?
if [ "$code" -gt 1 ]; then
echo "conversion not clean (exit $code) — see report.json" >&2
exit "$code"
fi

Where a policy needs to be sharper than the exit code, assert on the report itself. Fail on any unsupported content, but print what it was first:

Terminal window
jq -r '.features[] | select(.status == "unsupported")
| "\(.id) ×\(.count): \(.detail // "see docs")"' report.json
jq -e '[.features[] | select(.status == "unsupported")] | length == 0' report.json

Codes 4 and 5 are the real failures. On a resource-limit hit the partial outputs are deleted before the process exits, so a pipeline can never gate on a truncated PDF — and the report is still written, naming the limit, its configured value, and the measured one.

Two things the report will never contain

  • Cell contents. detail and message strings are static templates plus counts and locations. CI includes a leak test — fixtures with sentinel strings in their cells assert the sentinels never reach the output — so a report from a confidential workbook is safe to attach to a support mail. This is also what makes rendlio sheets diagnose usable on a workbook you are not allowed to send anywhere.
  • Licence data. Beyond options.licenseMode, which is either licensed:<tier> or evaluation, no licence information appears in a report.

Where the contract lives

  • Compatibility report — the v1 schema field by field, the warning-code ranges, an annotated example, and three levels of CI gating.
  • Quickstart — the five-minute path from install to a first PDF with a report next to it.
  • Security model — what happens when the workbook is not merely awkward but hostile.
Reading about it is the slow way

Try it on your workbook.

Drop a real file into the playground and read the compatibility report it comes back with — no account, no email, and the file is gone within the hour.