{"record":{"id":"7749ba291baed2de","repo":"OpenNHP/opennhp","slug":"failed-to-parse-ztdo-header-s","errorCode":null,"errorMessage":"failed to parse ztdo header:%s","messagePattern":"failed to parse ztdo header:(.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"endpoints/agent/udpagent.go","lineNumber":1379,"sourceCode":"\t\t\t}\n\n\t\t\tproviderPbk, _ := base64.StdEncoding.DecodeString(dataPrkWrapping.ProviderPublicKeyBase64)\n\n\t\t\tif dagMsg.AccessUrl == \"\" {\n\t\t\t\tlog.Error(\"access url is empty, please check with data provider\")\n\t\t\t\treturn \"\", fmt.Errorf(\"access url is empty, please check with data provider\")\n\t\t\t}\n\n\t\t\tvar err error\n\t\t\tztdoPath, err := utils.DownloadFileToTemp(dagMsg.AccessUrl, \"ztdo-\")\n\t\t\tif err != nil {\n\t\t\t\tlog.Error(\"failed to download ztdo: %v\\n\", err)\n\t\t\t\treturn \"\", fmt.Errorf(\"failed to download ztdo: %v\", err)\n\t\t\t}\n\n\t\t\tif parseErr := ztdo.ParseHeader(ztdoPath); parseErr != nil {\n\t\t\t\tfmt.Printf(\"Error: failed to parse ztdo header:%s\\n\", parseErr)\n\t\t\t\treturn \"\", fmt.Errorf(\"failed to parse ztdo header:%s\", parseErr)\n\t\t\t}\n\n\t\t\tif ztdoId != ztdo.GetObjectID() {\n\t\t\t\tfmt.Printf(\"Error: ztdo id mismatch, please check with data provider\\n\")\n\t\t\t\treturn \"\", fmt.Errorf(\"ztdo id mismatch, please check with data provider\")\n\t\t\t}\n\n\t\t\t// decrypt data private key\n\t\t\tsaDataPrk := ztdolib.NewSymmetricAgreement(ztdo.GetECCMode(), false)\n\t\t\tsaDataPrk.SetMessagePatterns(ztdolib.DataPrivateKeyWrappingPatterns)\n\t\t\tsaDataPrk.SetPsk([]byte(ztdolib.InitialDHPKeyWrappingString))\n\t\t\tsaDataPrk.SetStaticKeyPair(teeEcdh)\n\t\t\tsaDataPrk.SetEphemeralKeyPair(consumerEphemeralEcdh)\n\t\t\tsaDataPrk.SetRemoteStaticPublicKey(providerPbk)\n\n\t\t\tgcmKey, ad := saDataPrk.AgreeSymmetricKey()\n\n\t\t\tdataPrkBase64, err := dataPrkWrapping.Unwrap(gcmKey[:], ad)","sourceCodeStart":1361,"sourceCodeEnd":1397,"githubUrl":"https://github.com/OpenNHP/opennhp/blob/6e04ca5ff03222a699c24205cd4bf8fee9af7ffe/endpoints/agent/udpagent.go#L1361-L1397","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before: header parsed blind\nif parseErr := ztdo.ParseHeader(ztdoPath); parseErr != nil {\n    return \"\", fmt.Errorf(\"failed to parse ztdo header:%s\", parseErr)\n}\n\n// after: sanity-check size first\nif fi, err := os.Stat(ztdoPath); err != nil || fi.Size() < minZtdoHeaderSize {\n    return \"\", fmt.Errorf(\"downloaded file too small/corrupt, not a valid ztdo\")\n}\nif parseErr := ztdo.ParseHeader(ztdoPath); parseErr != nil {\n    return \"\", fmt.Errorf(\"failed to parse ztdo header:%s\", parseErr)\n}","handlingStrategy":"type-guard","validationCode":"fi, err := os.Stat(ztdoPath)\nif err != nil || fi.Size() < minZtdoHeaderSize {\n    return fmt.Errorf(\"downloaded payload is not a valid ztdo container\")\n}","typeGuard":"func looksLikeZtdo(path string) bool {\n    f, err := os.Open(path); if err != nil { return false }\n    defer f.Close()\n    magic := make([]byte, 4)\n    _, _ = io.ReadFull(f, magic)\n    return bytes.Equal(magic, ztdoMagicBytes)\n}","tryCatchPattern":"if err := ztdo.ParseHeader(ztdoPath); err != nil {\n    // quarantine file, re-download once, then report\n    return fmt.Errorf(\"invalid ztdo payload from provider: %w\", err)\n}","preventionTips":["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."],"tags":["ztdo","parsing","data-integrity","dhp"],"backgroundTag":"invalid-json-response","analyzedSha":"6e04ca5ff03222a699c24205cd4bf8fee9af7ffe","analyzedAt":"2026-09-07T15:44:59.941Z","contentChangedAt":"2026-09-07T15:44:59.941Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}