Troubleshooting

CmdCal uses a structured error system. Every engine error is a PaperError with machine-readable fields for programmatic handling.

PaperError Structure

TYPESCRIPT
class PaperError extends Error {
  readonly code: PaperErrorCode;
  readonly phase: ErrorPhase;
  readonly slideIndex?: number;
  readonly nodeId?: string;
}
  • code -- a stable string enum for switch/case handling.
  • phase -- which stage of the pipeline failed.
  • slideIndex -- the zero-based slide that caused the error (when applicable).
  • nodeId -- the specific AST node ID (when applicable).
  • cause -- the underlying error, if this error wraps another.

Catch and branch on code:

TYPESCRIPT
try {
  const pptx = await engine.renderDocument(doc);
} catch (err) {
  if (err instanceof PaperError) {
    switch (err.code) {
      case "FONT_NOT_FOUND":
        console.error(`Missing font on slide ${err.slideIndex}: ${err.message}`);
        break;
      case "RENDER_CANCELLED":
        console.log("Render was aborted");
        break;
      default:
        console.error(`[${err.code}] ${err.phase}: ${err.message}`);
    }
  }
}

Error Codes

Input and Validation

CodeDescriptionFix
VALIDATION_FAILEDThe PaperDocument failed schema validation.Check the error message for the specific field. Validate your document against the schema before rendering.
STRUCTURAL_VALIDATION_FAILEDThe rendered PPTX archive failed structural checks (broken relationship IDs, missing content types, malformed XML).Inspect the qualityReport.structuralValidation.checks array. If using repairMode: "structural", the engine attempts automatic repair.
DESKTOP_VALIDATION_FAILEDThe rendered PPTX failed PowerPoint desktop validation (hosted API only).Review the desktop validation artifacts: saved copy, screenshot, and PDF. The issue is typically a PowerPoint-specific rendering difference.
COMPATIBILITY_CONTRACT_VIOLATIONA slide required a fallback level that exceeds the requested outputMode. For example, a slide needs visual_fallback but you requested strict_editable.Simplify the slide content, switch to editable_preferred or visual_safe, or split complex slides.
VALIDATION_BACKEND_UNAVAILABLEDesktop validation (desktop_async or desktop_blocking) was requested but is not available in the current runtime. The self-hosted engine does not include a PowerPoint oracle.Use structural validation mode, or switch to the hosted API for desktop validation.
FEATURE_REQUIRES_UPGRADEA paid feature (e.g. template support) was used with @paperjsx/lite.Upgrade to the full @paperjsx/pptx-core package.

Resources and Media

CodeDescriptionFix
FONT_NOT_FOUNDA font family referenced in the document is not loaded in the font cache and no system fallback was found.Call loadFont() or loadFontWithHarfBuzz() before rendering. Check that the font file path is correct. The engine falls back to NotoSans if available.
MEDIA_FETCH_FAILEDAn image URL in the document could not be fetched (HTTP error, DNS failure, or timeout).Verify the URL is accessible from the rendering environment. Use HTTPS URLs. For self-hosted deployments behind a firewall, pre-fetch images and use base64 data URLs.
MEDIA_CORRUPTAn image was fetched successfully but could not be decoded (corrupt JPEG, unsupported format, zero-byte file).Re-export the image from its source application. Supported formats: JPEG, PNG, GIF, SVG, WebP.
RESOURCE_LIMIT_EXCEEDEDThe document exceeds engine limits (e.g. more than 200 slides, excessively large images).Reduce slide count or image dimensions. Split large presentations into multiple render jobs.

Runtime and Queue

CodeDescriptionFix
RENDER_CANCELLEDThe render was aborted via the AbortSignal passed in options. This fires both before the render starts (if the signal is already aborted) and while queued for the render mutex.This is expected behavior when you cancel a render. No fix needed.
RENDER_TIMEOUTThe render exceeded the allowed wall-clock time.Reduce slide complexity or split into smaller jobs. Check for very large charts or deeply nested node trees.
QUEUE_TIMEOUTThe render waited too long in the mutex queue.Reduce concurrent render requests. In self-hosted deployments, scale horizontally with more processes.
QUEUE_FULLThe render queue has reached capacity.Back off and retry. In self-hosted deployments, increase process count.
WASM_INIT_FAILEDThe Yoga (layout) or HarfBuzz (text shaping) WASM module failed to initialize.Check that the WASM files are accessible at runtime. In Docker, ensure the vendor/core directory is included in the image. In serverless environments, check bundle size limits.

Error Phases

The phase field tells you which pipeline stage failed:

PhaseDescription
validationDocument schema validation or pre-render checks
compilationCompiling high-level constructs into the internal AST
layoutYoga layout computation (positioning, sizing)
typographyFont loading, text measurement, HarfBuzz shaping
mediaImage fetching and decoding
chartChart data compilation or Excel embedding
serializationOOXML XML generation
archiveZIP archive assembly
wasm-initWASM module initialization (Yoga, HarfBuzz)
fontFont cache operations
templateTemplate parsing, layout mapping, or placeholder injection

Debug Logging

Inject a custom logger to capture engine diagnostics:

TYPESCRIPT
import { setLogger } from "@paperjsx/pptx-core";

setLogger({
  warn(message: string) {
    console.warn(`[cmdcal] ${message}`);
  },
  metric(name: string, value: number, tags?: Record<string, string>) {
    // Forward to your metrics system
    statsd.gauge(`cmdcal.${name}`, value, tags);
  },
  schemaError(error) {
    console.error(`Schema: ${error.schemaName} (${error.errorCount} issues)`, error.issues);
  },
});

The Logger interface:

  • warn(message) -- required. Receives diagnostic warnings (font fallbacks, deprecated usage, compatibility notes).
  • metric(name, value, tags) -- optional. Receives numeric metrics (render time, slide count, memory usage).
  • schemaError(error) -- optional. Receives schema validation failure details with issue paths and codes.

Deterministic Mode

Enable deterministic mode for reproducible output. This fixes ZIP timestamps and eliminates non-deterministic ordering, making the output byte-for-byte identical across runs with the same input:

TYPESCRIPT
import { setDeterministicMode } from "@paperjsx/pptx-core";

setDeterministicMode(true);

const pptx1 = await engine.renderDocument(doc);
const pptx2 = await engine.renderDocument(doc);
// pptx1 and pptx2 are byte-identical
Deterministic mode is useful for snapshot testing, CI diffing, and debugging non-determinism issues. It uses a fixed ZIP timestamp of `1980-01-01T00:00:02Z` to avoid edge-case bugs in some ZIP libraries that treat midnight as an invalid DOS timestamp.

Common Issues

WASM Cold Start

The first render in a Node.js process loads Yoga and HarfBuzz WASM modules, adding 200-500ms of latency. To mitigate this in serverless environments:

  • Use provisioned concurrency (AWS Lambda) or min-instances (Cloud Run) to keep warm instances available.
  • Run a lightweight warm-up render during initialization.

Font Not Found

The engine auto-loads system fonts and uses NotoSans as a last-resort fallback. If you see FONT_NOT_FOUND:

  1. Check that the font file exists at the expected path.
  2. On Linux/Docker, install the font package (e.g. fonts-noto-core) or copy .ttf files into /usr/share/fonts/.
  3. Call loadFont("Family Name", buffer) explicitly before rendering.
  4. Check the quality report's fontSubstitutions field to see which fonts were substituted.

Image Fetch Failures

MEDIA_FETCH_FAILED usually means the rendering environment cannot reach the image URL. Common causes:

  • The URL requires authentication (use pre-signed URLs or base64 data URLs).
  • DNS resolution fails in the container (check /etc/resolv.conf).
  • The server returns a redirect that the engine does not follow (use the final URL directly).
  • The request times out (the engine uses a fetch timeout; host images closer to the renderer).

Compatibility Contract Violations

If you get COMPATIBILITY_CONTRACT_VIOLATION with strict_editable:

  1. Check the quality report's slideReports to find which slide triggered the fallback.
  2. Look at the fallbackApplied.level and fallbackApplied.reason fields.
  3. Simplify the offending slide (reduce text density, avoid unsupported shape combinations, use standard chart types).
  4. Consider switching to editable_preferred if some anchored positioning is acceptable.

XLSX Troubleshooting

Workbook Opens With Repair Warnings

Start with:

TYPESCRIPT
const summary = await SpreadsheetEngine.validate(buffer);

Look for findings like:

  • missing content types
  • orphan relationships
  • invalid table refs
  • overlapping merges
  • invalid defined names

If the workbook is salvageable through the conservative Phase 5 rule set:

TYPESCRIPT
const repaired = await SpreadsheetEngine.validateAndRepair(buffer);

Review the repair actions before replacing the original artifact in your workflow.

Customer Template Behaves Strangely

Parse the template first instead of injecting blindly:

TYPESCRIPT
const template = await SpreadsheetEngine.parseTemplate(buffer);
const inspection = SpreadsheetEngine.inspectTemplate(template);

Check:

  • named range inventory
  • table inventory
  • sanitization actions
  • preserved opaque parts

If the inspection report shows stripped unsafe parts or broken anchors, treat that as a template cleanup task instead of assuming generation is wrong.

Large Workbook Is Slower Or Bigger Than Expected

Use:

  • SpreadsheetEngine.preflight(...)
  • SpreadsheetEngine.plan(...)
  • SpreadsheetEngine.renderWithMetrics(...)

Those surfaces tell you whether the issue is:

  • unique-string pressure
  • style cardinality
  • row chunking assumptions
  • large projected output size

MCP Spreadsheet Tooling

If generate_spreadsheet works but the follow-up tools do not, verify that:

  • the artifact path still exists on disk
  • the file was not moved out of PAPERJSX_OUTPUT_DIR
  • you are passing an .xlsx buffer or artifact path, not a template index or JSON document

For agent loops, keep the original artifact path and the repaired artifact path separately so validation and repair remain auditable.

Troubleshooting — CmdCal Docs