dgraph-io/dgraph · warning

same sha returned %d queries

Error message

same sha returned %d queries

What it means

After storing a new persisted query, ProcessPersistedQuery reads it back with a query on the sha256; it expects exactly one row. If len(shaQueryRes.Me) != 1 (0 or >1), it returns 'same sha returned %d queries'. This catches duplicate or missing stored entries for the same hash — the read-back isn't visible yet or duplicate hash nodes exist.

Source

Thrown at edgraph/graphql.go:131

								ObjectValue: &api.Value{Val: &api.Value_StrVal{
									StrVal: "dgraph.graphql.persisted_query"}},
							},
						},
					},
				},
				CommitNow: true,
			},
			doAuth: NoAuthorize,
		}

		ctx := context.WithValue(ctx, IsGraphql, true)
		_, err := (&Server{}).doQuery(ctx, req)
		return err

	}

	if len(shaQueryRes.Me) != 1 {
		return fmt.Errorf("same sha returned %d queries", len(shaQueryRes.Me))
	}

	gotQuery := ""
	if len(shaQueryRes.Me[0].PersistedQuery) >= 64 {
		gotQuery = shaQueryRes.Me[0].PersistedQuery[64:]
	}

	if len(query) > 0 && gotQuery != query {
		return errors.New("query does not match persisted query")
	}

	gqlReq.Query = gotQuery
	return nil

}

func hashMatches(query, sha256Hash string) (bool, error) {
	hasher := sha256.New()

View on GitHub (pinned to 759e242be6)

Solutions

  1. Retry the request; concurrent duplicate registration usually resolves on subsequent attempts
  2. Deduplicate stored PersistedQuery nodes for that hash (query all matching, keep one, delete extras)
  3. Ensure all requests use the same namespace so reads/writes hit the same persisted-query records
  4. Serialize initial client start-up (avoid many goroutines firing the same first query+hash simultaneously)

Example fix

// before: N workers each register the same query at cold start
workers.forEach(w => w.run(initialQuery))
// after: register once, share result
const ready = registerPersistedQuery(initialQuery, hash) // tolerate PersistedQueryNotFound/duplicate
workers.forEach(w => ready.then(() => w.run(initialQuery)))
Defensive patterns

Strategy: retry

Try / catch

async function registerOnce(query, hash, attempts = 3) {
  for (let i = 0; i < attempts; i++) {
    try {
      return await gql({ query, extensions: { persistedQuery: { version: 1, sha256Hash: hash } } })
    } catch (e) {
      if (/same sha returned/.test(e.message) && i < attempts - 1) {
        await new Promise(r => setTimeout(r, 50 * 2 ** i))
        continue
      }
      throw e
    }
  }
}

Prevention

When it happens

Trigger: First-time registration of a persisted query (query+hash pair processed, stored via mutation, then queried back) when the read returns 0 rows (eventual visibility/timing, wrong namespace context) or more than 1 row (duplicate PersistedQuery nodes for the same hash).

Common situations: Racing concurrent first requests that each try to register the same hash, creating duplicates; DropAll/restore leaving stale duplicates; the read-back query running against a different namespace than the write.

Related errors


AI-assisted analysis of dgraph-io/dgraph@759e242be6 (2026-09-01). Data as JSON: /api/errors/ca36fbd4770bccf1. Report an issue: GitHub.