Skip to content

Troubleshooting: signature and timestamp failures

These entries cover signature failures raised through NextPDF\Exception\SignatureException and NextPDF\Security\Signature\Exception\SignatureLevelUnreachableException. Each entry names the exact factory method or class, so you can confirm the cause from the message and getContext() instead of inferring it.

Wording note: the engine does not certify that a signature is valid or that a document is protected. It reports the failure it detected. Treat each resolution as a step to remove a reported cause.

Entry: PAdES level “B-LT” or “B-LTA” cannot be produced

Section titled “Entry: PAdES level “B-LT” or “B-LTA” cannot be produced”
  • Symptom. SignatureException with the message tail nextpdf/enterprise package is required for B-LT/B-LTA signatures.
  • Likely cause. The long-term validation capability provider is absent. B-LT and B-LTA embed revocation material and an archival timestamp, and that provider ships in nextpdf/enterprise.
  • Evidence / diagnosis. The factory SignatureException::ltvCapabilityMissing() produces this exact message. getContext() returns signature_level set to the level you attempted.
  • Resolution.
    1. Install the provider by running composer require nextpdf/enterprise.
    2. Run the sign call again.
    3. If you cannot install the provider, request B-B or B-T instead, which the core package can produce.
  • Related. Exception reference.

Entry: signature level is unreachable and the call is rejected

Section titled “Entry: signature level is unreachable and the call is rejected”
  • Symptom. SignatureLevelUnreachableException with a message in the form PAdES level "<x>" is unreachable (highest achievable: "<y>").
  • Likely cause. The requested conformance level needs infrastructure that is not available at sign time, most often a timestamp authority for B-T and above. The engine fails closed: it does not silently downgrade and then advertise the higher level.
  • Evidence / diagnosis. getContext() returns requestedLevel, highestAchievableLevel, and reason. The reason field identifies the infrastructure gap. This is the fail-closed default introduced to prevent a document that claims a level it does not meet.
  • Resolution.
    1. Read the reason field to identify the missing infrastructure.
    2. Supply the missing component. For example, configure a timestamp authority, and run the call again.
    3. To intentionally accept a lower level, pass allowDegradation: true to PadesOrchestrator. The call then produces highestAchievableLevel and reports the level it produced.
  • Related. Encryption and permissions.

Entry: timestamp authority client is required but absent

Section titled “Entry: timestamp authority client is required but absent”
  • Symptom. SignatureException with the tail TSA client is required for level <x> but none was provided.
  • Likely cause. A B-T, B-LT, or B-LTA request needs a timestamp authority client, and the orchestrator has none.
  • Evidence / diagnosis. The factory SignatureException::tsaRequired() produces this message; getContext() carries the attempted signature_level.
  • Resolution.
    1. Configure a timestamp authority client and pass it to the orchestrator.
    2. Re-run the call.
    3. To produce a level that does not need a timestamp, request B-B.
  • Related. Exception reference.

Entry: timestamp authority endpoint URL is empty

Section titled “Entry: timestamp authority endpoint URL is empty”
  • Symptom. SignatureException with the tail TSA endpoint URL is empty.
  • Likely cause. A timestamp authority client was created with an empty endpoint URL.
  • Evidence / diagnosis. The factory SignatureException::tsaUrlEmpty() produces this message. This is a configuration defect, not a network failure.
  • Resolution.
    1. Set a non-empty endpoint URL on the timestamp authority client, such as https://timestamp.example.com/tsa.
    2. If the requested level does not require timestamping, remove the timestamp authority client wiring instead.
    3. Run the call again.
  • Related. Exception reference.

Entry: signature placeholder is missing from the buffer

Section titled “Entry: signature placeholder is missing from the buffer”
  • Symptom. SignatureException with the tail no /Contents <…> field found in PDF buffer (signature placeholder missing).
  • Likely cause. The signing stage received a buffer with no reserved signature container, so it has nowhere to write the signature.
  • Evidence / diagnosis. The factory SignatureException::signatureContentsNotFound() produces this message.
  • Resolution.
    1. Make sure the signature field and its placeholder are written before the signing stage runs.
    2. Run the pipeline again so the placeholder exists when signing starts.
  • Related. Exception reference.

Entry: revocation status is unknown (OCSP responder declined)

Section titled “Entry: revocation status is unknown (OCSP responder declined)”
  • Symptom. SignatureException with the tail OCSP responder returned non-successful OCSPResponseStatus "<status>".
  • Likely cause. The Online Certificate Status Protocol (OCSP) responder did not return a successful status, so it produced no revocation assertion. The engine follows RFC 6960 §4.2.1, which it cites in source: a populated response body is permitted only for the successful (0) status. The engine refuses to treat a declined response as a trust-positive result.
  • Evidence / diagnosis. The factory SignatureException::nonSuccessfulOcspResponseStatus() produces this message and names the reported status, such as tryLater or internalError. A reserved or unknown status byte instead produces SignatureException::reservedOcspResponseStatus().
  • Resolution.
    1. Identify the status in the message. For a transient status such as tryLater, retry the revocation fetch later.
    2. For unauthorized or malformedRequest, verify the OCSP request URL and the certificate the responder expects.
    3. Do not suppress the failure to obtain a B-LT or B-LTA artifact; the revocation assertion is part of that level.
  • Related. Exception reference.

Entry: certificate chain entry fails to decode

Section titled “Entry: certificate chain entry fails to decode”
  • Symptom. SignatureException with the tail failed to base64-decode PEM body — input is not valid PEM.
  • Likely cause. A certificate chain entry is not valid Privacy-Enhanced Mail (PEM), typically because of truncation, a stray character, or a binary Distinguished Encoding Rules (DER) blob supplied where PEM was expected.
  • Evidence / diagnosis. The factory SignatureException::pemDecodingFailed() produces this message during chain assembly.
  • Resolution.
    1. Inspect each chain certificate for stray characters or truncation.
    2. Re-export the affected certificate in PEM form.
    3. Re-run the sign call.
  • Related. Encryption and permissions.

Entry: private key type does not match the algorithm

Section titled “Entry: private key type does not match the algorithm”
  • Symptom. SignatureException with the tail expected private key of type "<x>" for the configured algorithm but got "<y>".
  • Likely cause. The loaded private key does not match the configured signature algorithm, such as a Rivest-Shamir-Adleman (RSA) key with an Elliptic Curve Digital Signature Algorithm (ECDSA) selection.
  • Evidence / diagnosis. The factory SignatureException::unexpectedKeyType() produces this message and names both the expected and actual key class.
  • Resolution.
    1. Verify that the certificate and key pair match the algorithm you selected.
    2. Either change the algorithm selection to match the key, or load the key that matches the algorithm.
    3. Run the sign call again.
  • Related. Exception reference.

Entry: Ed25519 key or signature material is malformed

Section titled “Entry: Ed25519 key or signature material is malformed”
  • Symptom. SignatureException with a tail naming an Ed25519 length mismatch — for example Ed25519 signature length <n> ≠ expected 64 bytes, or Ed25519 round-trip self-verify failed.
  • Likely cause. The runtime cryptography build returned key or signature material of the wrong length, or a freshly produced signature failed verification against its own public key. The engine cites RFC 8032 §3.4 in source, which fixes a detached Ed25519 signature at 64 bytes. The engine aborts instead of emitting material it cannot self-verify.
  • Evidence / diagnosis. The relevant factories are SignatureException::ed25519SignatureMalformed(), ::ed25519RoundTripVerifyFailed(), ::ed25519KeyParseFailed(), ::ed25519SeedInvalid(), ::ed25519SecretKeyMalformed(), and ::ed25519PublicKeyInvalid(). Each names the observed length.
  • Resolution.
    1. Reinstall the libsodium PHP extension; a stripped or corrupt build is the documented cause of wrong-length material.
    2. Confirm the key is an Ed25519 key and OpenSSL is 1.1.1 or newer.
    3. Re-run the sign call.
  • Related. Exception reference.

Entry: archival timestamp dictionary was not emitted

Section titled “Entry: archival timestamp dictionary was not emitted”
  • Symptom. SignatureException with the tail no /Type /DocTimeStamp dictionary was emitted into the PDF buffer.
  • Likely cause. The B-LTA archival loop ran, but the document timestamp dictionary never reached the buffer. The artifact would be a half-written B-LTA, so the engine refuses to return it.
  • Evidence / diagnosis. The factory SignatureException::documentTimestampNotEmitted() produces this message. This post-condition failure is raised at finalize time.
  • Resolution.
    1. Treat the output as discarded; do not ship the partial artifact.
    2. Run the B-LTA pipeline again with a reachable timestamp authority.
    3. If the failure repeats, capture getContext() and the chained previous exception for a defect report.
  • Related. Exception reference.
  • These factories set cert_info to a subject or thumbprint only when one is available; an empty cert_info is expected for capability and configuration failures.
  • A B-LT or B-LTA request without a configured Hypertext Transfer Protocol (HTTP) client raises SignatureException::httpClientMissing(); the revocation fetch needs a PHP Standards Recommendation (PSR)-18 client passed to the orchestrator.
  • A certificate backed by a hardware security module (HSM) without a signer implementation raises SignatureException::hsmSignerMissing(); wire the signer onto the certificate before signing.

Glossary: PAdES level · revocation assertion