OpenNHP/opennhp · error
failed to parse ztdo header
Error message
failed to parse ztdo header:%s
What it means
ztdo.ParseHeader failed on the downloaded file, meaning the file at ztdoPath is not a valid ztdo container or its header is corrupt/unrecognized. ParseHeader validates the ztdo magic/header fields (version, ECC mode, object id, etc.) before any decryption happens.
Solutions
- Inspect the downloaded temp file (file type, first bytes) — if it is HTML/XML it is an error page, not data.
- Re-download the ztdo to rule out truncation; verify Content-Length matches file size.
- Confirm provider and agent use compatible ztdo format versions (upgrade endpoints together).
- Re-encrypt and re-upload the object at the provider if the stored blob is corrupt.
- Check proxy/middlebox stripping or altering the response body.
Example fix
// before: header parsed blind
if parseErr := ztdo.ParseHeader(ztdoPath); parseErr != nil {
return "", fmt.Errorf("failed to parse ztdo header:%s", parseErr)
}
// after: sanity-check size first
if fi, err := os.Stat(ztdoPath); err != nil || fi.Size() < minZtdoHeaderSize {
return "", fmt.Errorf("downloaded file too small/corrupt, not a valid ztdo")
}
if parseErr := ztdo.ParseHeader(ztdoPath); parseErr != nil {
return "", fmt.Errorf("failed to parse ztdo header:%s", parseErr)
} Defensive patterns
Strategy: type-guard
Validate before calling
fi, err := os.Stat(ztdoPath)
if err != nil || fi.Size() < minZtdoHeaderSize {
return fmt.Errorf("downloaded payload is not a valid ztdo container")
} Type guard
func looksLikeZtdo(path string) bool {
f, err := os.Open(path); if err != nil { return false }
defer f.Close()
magic := make([]byte, 4)
_, _ = io.ReadFull(f, magic)
return bytes.Equal(magic, ztdoMagicBytes)
} Try / catch
if err := ztdo.ParseHeader(ztdoPath); err != nil {
// quarantine file, re-download once, then report
return fmt.Errorf("invalid ztdo payload from provider: %w", err)
} Prevention
- Verify content-type/size against expected metadata before parsing.
- Keep provider and agent ztdo library versions in lockstep.
- Use checksums on object storage uploads to catch corruption early.
When it happens
Trigger: The URL served an error page (HTML/JSON) or truncated file instead of a ztdo binary; the file was modified/corrupted in transit or in storage; the provider wrote an incompatible ztdo version header.
Common situations: Pre-signed URL returning an XML S3 error document; partial download due to interrupted connection that the downloader didn't surface; provider upgraded the ztdo format while the agent ships an older parser; manual tampering with the stored object.
Understand the failure class
Background: "Invalid JSON response" and "Failed to parse response" errors: when an API answers 200 but the body isn't the JSON your library expected — this error's family across 28 libraries.
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- ztdo id mismatch, please check with data provider
- access url is empty, please check with data provider
- failed to unwrap data private key
- Failed to refresh SDP
- Error: fail to find policyId for ztdoId
AI-assisted analysis of OpenNHP/opennhp@6e04ca5ff0 (2026-09-07).
Data as JSON: /api/errors/7749ba291baed2de.
Report an issue: GitHub.
Appendix: source
Thrown at endpoints/agent/udpagent.go:1379
}
providerPbk, _ := base64.StdEncoding.DecodeString(dataPrkWrapping.ProviderPublicKeyBase64)
if dagMsg.AccessUrl == "" {
log.Error("access url is empty, please check with data provider")
return "", fmt.Errorf("access url is empty, please check with data provider")
}
var err error
ztdoPath, err := utils.DownloadFileToTemp(dagMsg.AccessUrl, "ztdo-")
if err != nil {
log.Error("failed to download ztdo: %v\n", err)
return "", fmt.Errorf("failed to download ztdo: %v", err)
}
if parseErr := ztdo.ParseHeader(ztdoPath); parseErr != nil {
fmt.Printf("Error: failed to parse ztdo header:%s\n", parseErr)
return "", fmt.Errorf("failed to parse ztdo header:%s", parseErr)
}
if ztdoId != ztdo.GetObjectID() {
fmt.Printf("Error: ztdo id mismatch, please check with data provider\n")
return "", fmt.Errorf("ztdo id mismatch, please check with data provider")
}
// decrypt data private key
saDataPrk := ztdolib.NewSymmetricAgreement(ztdo.GetECCMode(), false)
saDataPrk.SetMessagePatterns(ztdolib.DataPrivateKeyWrappingPatterns)
saDataPrk.SetPsk([]byte(ztdolib.InitialDHPKeyWrappingString))
saDataPrk.SetStaticKeyPair(teeEcdh)
saDataPrk.SetEphemeralKeyPair(consumerEphemeralEcdh)
saDataPrk.SetRemoteStaticPublicKey(providerPbk)
gcmKey, ad := saDataPrk.AgreeSymmetricKey()
dataPrkBase64, err := dataPrkWrapping.Unwrap(gcmKey[:], ad)View on GitHub (pinned to 6e04ca5ff0)