{"record":{"id":"6f7cb053d543ac3c","repo":"dgraph-io/dgraph","slug":"provided-sha-does-not-match-query","errorCode":null,"errorMessage":"provided sha does not match query","messagePattern":"provided sha does not match query","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"edgraph/graphql.go","lineNumber":97,"sourceCode":"\t\t\tPersistedQuery string `json:\"dgraph.graphql.p_query\"`\n\t\t} `json:\"me\"`\n\t}\n\n\tshaQueryRes := &shaQueryResponse{}\n\tif len(storedQuery.Json) > 0 {\n\t\tif err := json.Unmarshal(storedQuery.Json, shaQueryRes); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tif len(shaQueryRes.Me) == 0 {\n\t\tif query == \"\" {\n\t\t\treturn errors.New(\"PersistedQueryNotFound\")\n\t\t}\n\t\tif match, err := hashMatches(query, sha256Hash); err != nil {\n\t\t\treturn err\n\t\t} else if !match {\n\t\t\treturn errors.New(\"provided sha does not match query\")\n\t\t}\n\n\t\treq = &Request{\n\t\t\treq: &api.Request{\n\t\t\t\tMutations: []*api.Mutation{\n\t\t\t\t\t{\n\t\t\t\t\t\tSet: []*api.NQuad{\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tSubject:     \"_:a\",\n\t\t\t\t\t\t\t\tPredicate:   \"dgraph.graphql.p_query\",\n\t\t\t\t\t\t\t\tObjectValue: &api.Value{Val: &api.Value_StrVal{StrVal: join}},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tSubject:   \"_:a\",\n\t\t\t\t\t\t\t\tPredicate: \"dgraph.type\",\n\t\t\t\t\t\t\t\tObjectValue: &api.Value{Val: &api.Value_StrVal{\n\t\t\t\t\t\t\t\t\tStrVal: \"dgraph.graphql.persisted_query\"}},\n\t\t\t\t\t\t\t},","sourceCodeStart":79,"sourceCodeEnd":115,"githubUrl":"https://github.com/dgraph-io/dgraph/blob/759e242be62c91f8d084da06ad0c8d21256d9c07/edgraph/graphql.go#L79-L115","documentation":"When a persisted-query request includes both the query text and a sha256Hash, the server recomputes the hash of the query via hashMatches; if it doesn't equal the client-supplied hash, the request is rejected with 'provided sha does not match query'. This protects against a client sending a hash that would poison the persisted-query cache.","triggerScenarios":"POST with `query` plus extensions.persistedQuery.sha256Hash where sha256(query) != provided hash — e.g. client hashes a different/normalized version of the query (print() output), or uses md5.","commonSituations":"Apollo client transforming/printing the document before hashing while sending the original string (or vice versa); hand-written clients hashing with wrong algorithm or including trailing whitespace; duplicated persisted-query logic across client versions.","solutions":["Compute the hash as lowercase hex sha256 of the exact query string sent, byte-for-byte","Send the same document object that was hashed (use graphql print/query literal from one source of truth)","Remove the extensions.persistedQuery field and just send the plain query if you don't need persistence","Upgrade/misconfigured client library — align hashing between all client versions"],"exampleFix":"// before\nconst query = 'query Me { me { name } }\\n'\nconst hash = crypto.createHash('md5').update(query.trim()).digest('hex') // wrong algo/input\n// after\nconst crypto = require('crypto')\nconst hash = crypto.createHash('sha256').update(query, 'utf8').digest('hex')\n// send { query, extensions: { persistedQuery: { version: 1, sha256Hash: hash } } }","handlingStrategy":"validation","validationCode":"const crypto = require('crypto')\nfunction persistedExtension(query) {\n  const sha256Hash = crypto.createHash('sha256').update(query, 'utf8').digest('hex')\n  return { version: 1, sha256Hash }\n}\n// always derive hash from the exact string you send:\ngql({ query, extensions: { persistedQuery: persistedExtension(query) } })","typeGuard":null,"tryCatchPattern":"try {\n  return await gql({ query, extensions: { persistedQuery: { version: 1, sha256Hash: hash } } })\n} catch (e) {\n  if (/does not match query/.test(e.message)) {\n    // fall back to plain query without the persistedQuery extension\n    return gql({ query })\n  }\n  throw e\n}","preventionTips":["Hash and send the identical query string from one source (never normalize one and send the other)","Use sha256 hex, not md5 or base64","Pin and align persisted-query client library versions"],"tags":["graphql","persisted-queries","sha256","hash-mismatch"],"backgroundTag":"persisted-query-hash-mismatch","analyzedSha":"759e242be62c91f8d084da06ad0c8d21256d9c07","analyzedAt":"2026-09-01T14:42:12.034Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}