Skip to content
getnextpdf.com

Compliance errors

These entries cover the two exceptions in the NextPDF\Compliance\Exception namespace. Both are thrown by the compliance subsystem: the canonical clause-hash pipeline and the compliance lifecycle (the Document Compliance Evidence cache, the audited-to-terminal promoter, and the cooldown observer).

Both classes are final and extend NextPdfException, which itself extends \RuntimeException and implements ContextAwareExceptionInterface. Neither subclass overrides getContext(), so both inherit the base implementation, which returns an empty array. The diagnostic detail is carried in the exception message, not in getContext(). Catch either type as NextPdfException, or as \RuntimeException if you have existing handlers.

  • When it is thrown. From ClauseHash::compute() when the host PHP runtime is missing a hard requirement of the canonical clause-hash pipeline. Currently the only such requirement is ext-intl, used for Unicode Normalization Form KC (NFKC) normalization. The single throw site raises the message ClauseHash requires ext-intl for NFKC normalisation.
  • Why it fails closed. composer.json mandates ext-intl, so this fires only in a misconfigured downstream that packages NextPDF without intl. The pipeline refuses to compute a non-NFKC-normalized digest rather than silently emit a hash that is incompatible with every other clause-hash consumer.
  • Context. getContext() returns an empty array. The cause is stated in the message.
  • Recovery. Operator action: install and enable the PHP intl extension on the host, then retry. There is no in-code workaround; the normalized digest cannot be produced without it.
  • When it is thrown. From the compliance lifecycle subsystem when a structural invariant on claims.json or its persistence pipeline is violated at runtime. Throw sites span the audited-to-terminal promoter, the cooldown observer, and the Document Compliance Evidence incremental cache. The concrete triggers are:
    • Malformed root documentclaims.json does not decode to an object, or claims.standards is not a JSON object (for example, claims.json must decode to an object, claims.standards must be a JSON object, claims.json invalid JSON: <detail>).
    • Read failuresclaims.json is missing or unreadable (for example, claims.json not found at: <path>, claims.json unreadable at <path>).
    • Atomic-write I/O failures in the persistence pipeline — temp open, flock, short write, fflush, atomic rename, and sidecar write (for example, tmp open failed at <path>, tmp flock failed at <path>, tmp write short for <path>, tmp fflush failed at <path>, atomic rename failed for <path>, sha256 sidecar write failed at <path>).
    • Encoder-side failuresjson_encode() rejects the outgoing payload (for example, claims.json encode failed: <detail>).
    • Evidence-cache bootstrap failures — the incremental cache cannot create or write its root directory (for example, IncrementalEvidenceCache: cannot create rootDir <path>, IncrementalEvidenceCache: write failed for <path>, IncrementalEvidenceCache: rename failed for <path>).
    • Internal invariant breaches — for example a gmdate produced empty timestamp or fqClauseId: empty clauseKey.
  • Why it exists. This is a domain-typed replacement for the previous use of \RuntimeException in the lifecycle and cache code. It does not change the runtime contract, because NextPdfException already extends \RuntimeException; existing catch (\RuntimeException $e) clauses keep working.
  • Context. getContext() returns an empty array. The offending path and the specific failure are named in the message; JSON-decode and encode failures also chain the underlying \JsonException as the previous exception, so read getPrevious() for those.
  • Recovery.
    • For shape and read errors, this is an operator action: inspect claims.json at the path named in the message, confirm it is well-formed JSON whose root and standards member are objects, and confirm it is present and readable.
    • For atomic-write and cache-bootstrap errors, check the target directory’s existence, permissions, and free space, then retry.
    • For encoder-side failures, this is a developer action: the payload handed to json_encode() is not encodable. Capture the chained previous exception and the message for a defect report.