unionlabs/union · warning · Error
Packet not found
Error message
Packet not found
What it means
Thrown in the writeData callback of a TanStack Query (Svelte) that polls a GraphQL v2_packets query every 30 seconds for a given packet_hash. It fires when the query succeeds but returns an empty v2_packets array, meaning the backend currently has no packet with that hash. The '// TODO: make tagged error' comment shows the authors know this generic Error conflates 'not indexed yet' with 'does not exist'.
Source
Thrown at app2/src/lib/queries/packet-details.svelte.ts:77
timestamp
transaction_hash
chain {
universal_chain_id
rpc_type
}
}
}
}
`),
variables: { packet_hash: packetHash },
refetchInterval: "30 seconds",
writeData: data => {
data.pipe(
Option.map(d => {
if (d.v2_packets.length === 0) {
// TODO: make tagged error
throw new Error("Packet not found")
}
return d.v2_packets[0]
}),
Option.tap(packet => {
packetDetails.data = Option.some(packet)
return Option.some(packet)
}),
)
},
writeError: error => {
packetDetails.error = error
},
})
View on GitHub (pinned to 031785bb6d)
Solutions
- Verify the hash is the full 66-character 0x-prefixed value returned by the sending transaction and belongs to the network the GraphQL endpoint indexes
- If the packet was just sent, treat this as transient: the 30-second refetchInterval recovers automatically once the indexer ingests the packet
- Run the v2_packets query directly in the GraphQL playground with the same packet_hash variable to confirm whether the row exists server-side
- Replace the generic Error with a tagged error (per the TODO) so UI code can distinguish 'pending indexing' from 'never exists' and stop retrying
Example fix
// before
if (d.v2_packets.length === 0) {
// TODO: make tagged error
throw new Error("Packet not found")
}
return d.v2_packets[0]
// after
export class PacketNotFoundError extends Error {
readonly _tag = "PacketNotFoundError" as const
}
if (d.v2_packets.length === 0) {
throw new PacketNotFoundError()
}
return d.v2_packets[0] Defensive patterns
Strategy: retry
Validate before calling
const isPacketHash = (h: string) => /^0x[0-9a-fA-F]{64}$/.test(h)
if (!isPacketHash(packetHash)) {
throw new Error(`Invalid packet hash: ${packetHash}`)
}
// only then start the polling query Type guard
const hasPacket = (d: { v2_packets: unknown[] } | undefined): d is { v2_packets: [unknown, ...unknown[]] } =>
(d?.v2_packets?.length ?? 0) > 0 Try / catch
try {
await queryClient.refetchQueries({ queryKey: ["packetDetails", packetHash] })
} catch (e) {
if (e instanceof Error && e.message === "Packet not found") {
// transient: packet not indexed yet — keep polling, do not surface as fatal
return
}
throw e
} Prevention
- Validate the hash format before starting the query
- Treat 'not found' on a freshly broadcast transaction as pending, not fatal
- Introduce a tagged error so retry/UI code can match on the specific case instead of the message string
When it happens
Trigger: Querying packet details with (a) a mistyped or truncated hash, (b) a packet that was just broadcast and is not yet indexed (the throw repeats on each 30s refetch until the indexer catches up), or (c) a hash from a different network than the GraphQL endpoint serves.
Common situations: Fresh cross-chain transactions where the packet lands on-chain seconds before the subgraph sees it; copying a hash without the 0x prefix or with missing characters; pointing the app at testnet data while using a mainnet hash.
AI-assisted analysis of unionlabs/union@031785bb6d (2026-08-16).
Data as JSON: /api/errors/2020c7dde0361bb5.
Report an issue: GitHub.