In my golang oracle, I need to get the TransactionReceipt for my transaction.

To do so I call client.TransactionReceipt(context.TODO(), txHash)

This yields a transaction receipt when the node is geth but fails when the node is parity. The error message is json: cannot unmarshal non-string into Go struct field Receipt.root of type hexutil.Bytes

I'm guessing this has something to do with the JSON returned by the nodes but I can't see any errors.

The JSON returned by GETH is:

  blockHash: "0x88ed2d126dc004b56309da73a897dee7601a03fa6aa4133f8b942646fe71b995",
  blockNumber: 1,
  contractAddress: null,
  cumulativeGasUsed: 21000,
  gasUsed: 21000,
  logs: [],
  transactionHash: "0x182eb16fa75d58e16b2ac13ba3697ad00a2a961f2d7cbb6c15e3c17fd262c267",
  transactionIndex: 0

and the one returned by Parity is:

  blockHash: "0x28f5afbde2ffe91f409f91b27ebe75d963326bfa79e72f29492692d06f48427a",
  blockNumber: 643,
  contractAddress: null,
  cumulativeGasUsed: 21000,
  gasUsed: 21000,
  logs: [],
  logsBloom: "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  root: null,
  status: null,
  transactionHash: "0xe7cf7d79c52fdec083706b51dca3aa3374b00a3d8276708e632387f8b1cdbfc1",
  transactionIndex: 0

The only difference I can see are the 2 additional fields root and status but these exist in the type declaration of the receipt:

type Receipt struct {
    // Consensus fields
    PostState         []byte   `json:"root"`
    Status            uint     `json:"status"`
    CumulativeGasUsed *big.Int `json:"cumulativeGasUsed" gencodec:"required"`
    Bloom             Bloom    `json:"logsBloom"         gencodec:"required"`
    Logs              []*Log   `json:"logs"              gencodec:"required"`

    // Implementation fields (don't reorder!)
    TxHash          common.Hash    `json:"transactionHash" gencodec:"required"`
    ContractAddress common.Address `json:"contractAddress"`
    GasUsed         *big.Int       `json:"gasUsed" gencodec:"required"`

1 Answer 1


The error comes from the json unmarshaller.

The Receipt provides the UnmarshalJSON method which is used by the json std lib to decode the bytes. If we look at the method definition we can see that:

  • it is using a different local Receipt type to do automatic decoding.
  • the local type has field PostState hexutil.Bytes which maps to the "root" JSON key (look at the json:"root" tag).

In the parity log root has value null and json unmarshaller tries to decode it into hexutil.Bytes and then convert it into []byte. hexutil.Bytes is just a redefinition of []byte which provides custom decoding, which fails if the source doesn't satisfy isString predicate:

func isString(input []byte) bool {
    return len(input) >= 2 && input[0] == '"' && input[len(input)-1] == '"'

Since this is a custom decoder, it passes input as raw bytes. In case of parity log it's ([]byte("null")) - it doesn't start (and end) with quotemark ("), so it fails.

The hexutil library has a specific test-case for that so it doesn't seem to be a bug, but a desired feature.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.