Skip to content

debug_* API: block/state dump, trace, and diagnostic endpoints diverge from the documented Go signatures, wire schemas, and console examples #35545

Description

@BenWhite713

1. debug_accountRange: debug_accountRange silently clamps nonpositive or over-256 maxResults instead of returning the requested page size

  • Statement: debug_accountRange silently clamps nonpositive or over-256 maxResults instead of returning the requested page size.
  • URL: https://geth.ethereum.org/docs/interacting-with-geth/rpc/ns-debug
  • Code location:
    • eth/api_debug.go:138-155
      func (api *DebugAPI) AccountRange(blockNrOrHash rpc.BlockNumberOrHash, start hexutil.Bytes, maxResults int, nocode, nostorage, incompletes bool) (state.Dump, error) {
      var stateDb *state.StateDB
      var err error
      if number, ok := blockNrOrHash.Number(); ok {
      if number == rpc.PendingBlockNumber {
      // If we're dumping the pending state, we need to request
      // both the pending block as well as the pending state from
      // the miner and operate on those
      _, _, stateDb = api.eth.miner.Pending()
      if stateDb == nil {
      return state.Dump{}, errors.New("pending state is not available")
      }
      } else {
      var header *types.Header
      switch number {
      case rpc.LatestBlockNumber:
      header = api.eth.blockchain.CurrentBlock()
  • Description: Root cause — DebugAPI.AccountRange accepts maxResults as a plain int with no bounds-validation error path; the method treats it as an internal "sanity limit" over RPC rather than a validated request parameter, so nonpositive or over-256 values are clamped to a default/maximum bound before the account iteration runs, instead of the call failing or honoring the exact page size the caller asked for.
  • Method: debug_accountRange

2. debug_chaindbProperty: The documented debug_chaindbProperty Console/RPC signatures retain a property argument that DebugAPI.ChaindbProperty no longer has

  • Statement: The documented debug_chaindbProperty Console/RPC signatures retain a property argument that DebugAPI.ChaindbProperty no longer has.
  • URL: https://geth.ethereum.org/docs/interacting-with-geth/rpc/ns-debug
  • Code location:
    • internal/ethapi/api.go:2119-2121
      func (api *DebugAPI) ChaindbProperty() (string, error) {
      return api.b.ChainDb().Stat()
      }
    • eth/api_debug.go:52-69
      func (api *DebugAPI) DumpBlock(blockNr rpc.BlockNumber) (state.Dump, error) {
      opts := &state.DumpConfig{
      OnlyWithAddresses: true,
      Max: AccountRangeMaxResults, // Sanity limit over RPC
      }
      if blockNr == rpc.PendingBlockNumber {
      // If we're dumping the pending state, we need to request
      // both the pending block as well as the pending state from
      // the miner and operate on those
      _, _, stateDb := api.eth.miner.Pending()
      if stateDb == nil {
      return state.Dump{}, errors.New("pending state is not available")
      }
      return stateDb.RawDump(opts), nil
      }
      var header *types.Header
      switch blockNr {
      case rpc.LatestBlockNumber:
  • Description: Root cause — DebugAPI.ChaindbProperty is declared with zero parameters (func (api *DebugAPI) ChaindbProperty() (string, error)) and simply forwards to the database's Stat() call; the Go method's property string argument was removed at some point without updating the documented Console (debug.chaindbProperty(property string)) and raw-RPC (params: [property]) signatures, so a caller that follows the documented arity supplies an argument the current handler no longer accepts.
  • Method: debug_chaindbProperty

3. debug_dumpBlock: The debug_dumpBlock Console documentation copies debug_traceBlockByHash with an unsupported options argument instead of the DumpBlock invocation

  • Statement: The debug_dumpBlock Console documentation copies debug_traceBlockByHash with an unsupported options argument instead of the DumpBlock invocation.
  • URL: https://geth.ethereum.org/docs/interacting-with-geth/rpc/ns-debug
  • Code location:
    • eth/api_debug.go:52-69
      func (api *DebugAPI) DumpBlock(blockNr rpc.BlockNumber) (state.Dump, error) {
      opts := &state.DumpConfig{
      OnlyWithAddresses: true,
      Max: AccountRangeMaxResults, // Sanity limit over RPC
      }
      if blockNr == rpc.PendingBlockNumber {
      // If we're dumping the pending state, we need to request
      // both the pending block as well as the pending state from
      // the miner and operate on those
      _, _, stateDb := api.eth.miner.Pending()
      if stateDb == nil {
      return state.Dump{}, errors.New("pending state is not available")
      }
      return stateDb.RawDump(opts), nil
      }
      var header *types.Header
      switch blockNr {
      case rpc.LatestBlockNumber:
    • rpc/types.go:80-97

      go-ethereum/rpc/types.go

      Lines 80 to 97 in 81ab8b5

      func (bn *BlockNumber) UnmarshalJSON(data []byte) error {
      input := strings.TrimSpace(string(data))
      if len(input) >= 2 && input[0] == '"' && input[len(input)-1] == '"' {
      input = input[1 : len(input)-1]
      }
      switch input {
      case "earliest":
      *bn = EarliestBlockNumber
      return nil
      case "latest":
      *bn = LatestBlockNumber
      return nil
      case "pending":
      *bn = PendingBlockNumber
      return nil
      case "finalized":
      *bn = FinalizedBlockNumber
  • Description: Root cause — DebugAPI.DumpBlock(blockNr rpc.BlockNumber) (state.Dump, error) takes a single block-number selector decoded by rpc.BlockNumber.UnmarshalJSON and has no second (options) parameter at all; the Console doc example was authored by copy-pasting the neighboring debug_traceBlockByHash(hash, options) snippet instead of describing the real one-argument DumpBlock call, so it documents an argument the handler cannot accept.
  • Method: debug_dumpBlock

4. debug_dumpBlock: debug_dumpBlock returns state.Dump rather than the documented state.World

  • Statement: debug_dumpBlock returns state.Dump rather than the documented state.World.
  • URL: https://geth.ethereum.org/docs/interacting-with-geth/rpc/ns-debug
  • Code location:
    • eth/api_debug.go:52-69
      func (api *DebugAPI) DumpBlock(blockNr rpc.BlockNumber) (state.Dump, error) {
      opts := &state.DumpConfig{
      OnlyWithAddresses: true,
      Max: AccountRangeMaxResults, // Sanity limit over RPC
      }
      if blockNr == rpc.PendingBlockNumber {
      // If we're dumping the pending state, we need to request
      // both the pending block as well as the pending state from
      // the miner and operate on those
      _, _, stateDb := api.eth.miner.Pending()
      if stateDb == nil {
      return state.Dump{}, errors.New("pending state is not available")
      }
      return stateDb.RawDump(opts), nil
      }
      var header *types.Header
      switch blockNr {
      case rpc.LatestBlockNumber:
    • core/state/dump.go:241-248
      func (s *StateDB) RawDump(opts *DumpConfig) Dump {
      dump := &Dump{
      Accounts: make(map[string]DumpAccount),
      }
      next, _ := s.DumpToCollector(dump, opts)
      dump.Next = next
      return *dump
      }
  • Description: Root cause — DebugAPI.DumpBlock's declared return type is (state.Dump, error), and every code path (including the pending-state branch) returns the value produced by StateDB.RawDump, whose signature is func (s *StateDB) RawDump(opts *DumpConfig) Dump. No state.World type is constructed or returned anywhere in the call chain; state.World is a stale type name left over from a prior API shape that the documentation never updated.
  • Method: debug_dumpBlock

5. debug_setHead: debug_setHead takes hexutil.Uint64 and therefore uses hex-quantity decoding, not the documented plain uint64 Go signature

  • Statement: debug_setHead takes hexutil.Uint64 and therefore uses hex-quantity decoding, not the documented plain uint64 Go signature.
  • URL: https://geth.ethereum.org/docs/interacting-with-geth/rpc/ns-debug
  • Code location:
    • internal/ethapi/api.go:2145-2154
      func (api *DebugAPI) SetHead(number hexutil.Uint64) error {
      header := api.b.CurrentHeader()
      if header == nil {
      return errors.New("current header is not available")
      }
      if header.Number.Uint64() <= uint64(number) {
      return errors.New("not allowed to rewind to a future block")
      }
      return api.b.SetHead(uint64(number))
      }
  • Description: Root cause — DebugAPI.SetHead(number hexutil.Uint64) error declares its parameter as hexutil.Uint64, a type whose JSON unmarshaler requires a 0x-prefixed hex string and rejects a plain decimal JSON number. The documented plain-uint64 Go signature describes a type that would accept a decimal number, which does not match the type actually wired into the JSON-RPC method table.
  • Method: debug_setHead

6. debug_stacks: debug_stacks can apply an optional filter and return matching goroutine stacks rather than all stacks

  • Statement: debug_stacks can apply an optional filter and return matching goroutine stacks rather than all stacks.
  • URL: https://geth.ethereum.org/docs/interacting-with-geth/rpc/ns-debug
  • Code location:
    • internal/debug/api.go:192-209
      func (*HandlerT) Stacks(filter *string) string {
      buf := new(bytes.Buffer)
      pprof.Lookup("goroutine").WriteTo(buf, 2)
      // If any filtering was requested, execute them now
      if filter != nil && len(*filter) > 0 {
      expanded := *filter
      // The input filter is a logical expression of package names. Transform
      // it into a proper boolean expression that can be fed into a parser and
      // interpreter:
      //
      // E.g. (eth || snap) && !p2p -> (eth in Value || snap in Value) && p2p not in Value
      expanded = regexp.MustCompile(`[:/\.A-Za-z0-9_-]+`).ReplaceAllString(expanded, "`$0` in Value")
      expanded = regexp.MustCompile("!(`[:/\\.A-Za-z0-9_-]+`)").ReplaceAllString(expanded, "$1 not")
      expanded = strings.ReplaceAll(expanded, "||", "or")
      expanded = strings.ReplaceAll(expanded, "&&", "and")
      log.Info("Expanded filter expression", "filter", *filter, "expanded", expanded)
  • Description: Root cause — HandlerT.Stacks(filter *string) string accepts an optional filter argument that, when non-empty, is compiled into a boolean expression over goroutine package names ((eth || snap) && !p2p style syntax) and used to select which goroutine stacks from pprof.Lookup("goroutine") are written into the response, rather than always dumping every goroutine's stack unconditionally.
  • Method: debug_stacks

7. debug_traceBlockByHash: debug_traceBlockByHash returns a slice of transaction trace results rather than BlockTraceResult

  • Statement: debug_traceBlockByHash returns a slice of transaction trace results rather than BlockTraceResult.
  • URL: https://geth.ethereum.org/docs/interacting-with-geth/rpc/ns-debug
  • Code location:
    • eth/tracers/api.go:588-605
      // process that generates states in one thread and traces txes
      // in separate worker threads.
      if config != nil && config.Tracer != nil && *config.Tracer != "" {
      if isJS := DefaultDirectory.IsJS(*config.Tracer); isJS {
      return api.traceBlockParallel(ctx, block, statedb, config)
      }
      }
      // Native tracers have low overhead
      var (
      txs = block.Transactions()
      blockHash = block.Hash()
      signer = types.MakeSigner(api.backend.ChainConfig(), block.Number(), block.Time())
      results = make([]*txTraceResult, len(txs))
      )
      for i, tx := range txs {
      // Generate the next state snapshot fast without tracing
      msg, _ := core.TransactionToMessage(tx, signer, block.BaseFee())
      txctx := &Context{
    • common/types.go:195-204

      go-ethereum/common/types.go

      Lines 195 to 204 in 81ab8b5

      func (h *Hash) UnmarshalGraphQL(input interface{}) error {
      var err error
      switch input := input.(type) {
      case string:
      err = h.UnmarshalText([]byte(input))
      default:
      err = fmt.Errorf("unexpected type %T for Hash", input)
      }
      return err
      }
  • Description: Root cause — API.traceBlock allocates results := make([]*txTraceResult, len(txs)) and populates one entry per transaction, and this slice is what is ultimately returned to the JSON-RPC layer for debug_traceBlockByHash. There is no BlockTraceResult wrapper struct anywhere in the trace path; the response shape has always been a flat, per-transaction array, so the documented named type does not correspond to any type constructed by the implementation.
  • Method: debug_traceBlockByHash

8. debug_traceCall: The debug_traceCall curl example selects pending even though tracing on pending errors before any documented successful result fields can be produced

  • Statement: The debug_traceCall curl example selects pending even though tracing on pending errors before any documented successful result fields can be produced.
  • URL: https://geth.ethereum.org/docs/interacting-with-geth/rpc/ns-debug
  • Code location:
    • eth/tracers/api.go:895-912
      // it can be done with block- and state-overrides instead, which offers
      // more flexibility and stability than trying to trace on 'pending', since
      // the contents of 'pending' is unstable and probably not a true representation
      // of what the next actual block is likely to contain.
      return nil, errors.New("tracing on top of pending is not supported")
      }
      block, err = api.blockByNumber(ctx, number)
      } else {
      return nil, errors.New("invalid arguments; neither block nor hash specified")
      }
      if err != nil {
      return nil, err
      }
      // try to recompute the state
      if config != nil && config.TxIndex != nil {
      _, _, statedb, release, err = api.backend.StateAtTransaction(ctx, block, int(*config.TxIndex))
      } else {
      statedb, release, err = api.backend.StateAtBlock(ctx, block, nil, true, false)
    • eth/tracers/api.go:1009-1026

      go-ethereum/eth/tracers/api.go

      Lines 1009 to 1026 in 81ab8b5

      <-deadlineCtx.Done()
      if errors.Is(deadlineCtx.Err(), context.DeadlineExceeded) {
      tracer.Stop(errors.New("execution timeout"))
      // Stop evm execution. Note cancellation is not necessarily immediate.
      evm.Cancel()
      }
      }()
      defer cancel()
      // Call Prepare to clear out the statedb access list
      statedb.SetTxContext(txctx.TxHash, txctx.TxIndex, uint32(txctx.TxIndex+1))
      _, _, err = core.ApplyTransactionWithEVM(message, core.NewGasPool(message.GasLimit), statedb, vmctx.BlockNumber, txctx.BlockHash, vmctx.Time, tx, evm)
      if err != nil {
      return nil, fmt.Errorf("tracing failed: %w", err)
      }
      return tracer.GetResult()
      }
  • Description: Root cause — API.TraceCall explicitly short-circuits with return nil, errors.New("tracing on top of pending is not supported") whenever the block selector resolves to the pending block, before any state is captured or traceTx is invoked. The documented curl example builds its request against "pending", so following it verbatim can never reach the successful trace-result fields the surrounding documentation describes, because the pending branch always errors first.
  • Method: debug_traceCall

9. debug_traceTransaction: TraceConfig exposes no reexec option, so neither its existence nor a uint64 type is valid for debug_traceTransaction

  • Statement: TraceConfig exposes no reexec option, so neither its existence nor a uint64 type is valid for debug_traceTransaction.
  • URL: https://geth.ethereum.org/docs/interacting-with-geth/rpc/ns-debug
  • Code location:
    • eth/tracers/api.go:240-257
      func (api *API) traceChain(start, end *types.Block, config *TraceConfig, closed <-chan error) chan *blockTraceResult {
      blocks := int(end.NumberU64() - start.NumberU64())
      threads := runtime.NumCPU()
      if threads > blocks {
      threads = blocks
      }
      var (
      pend = new(sync.WaitGroup)
      ctx = context.Background()
      taskCh = make(chan *blockTraceTask, threads)
      resCh = make(chan *blockTraceTask, threads)
      tracker = newStateTracker(maximumPendingTraceStates, start.NumberU64())
      )
      for th := 0; th < threads; th++ {
      pend.Add(1)
      go func() {
      defer pend.Done()
    • eth/tracers/api.go:837-854
      func (api *API) TraceTransaction(ctx context.Context, hash common.Hash, config *TraceConfig) (interface{}, error) {
      found, _, blockHash, blockNumber, index := api.backend.GetCanonicalTransaction(hash)
      if !found {
      // Warn in case tx indexer is not done.
      if !api.backend.TxIndexDone() {
      return nil, ethapi.NewTxIndexingError()
      }
      // Only mined txes are supported
      return nil, errTxNotFound
      }
      // It shouldn't happen in practice.
      if blockNumber == 0 {
      return nil, errors.New("genesis is not traceable")
      }
      block, err := api.blockByNumberAndHash(ctx, rpc.BlockNumber(blockNumber), blockHash)
      if err != nil {
      return nil, err
      }
  • Description: Root cause — API.TraceTransaction(ctx, hash, config *TraceConfig) decodes its config parameter as *TraceConfig, and re-execution depth in the current implementation is governed internally (e.g. by traceChain's worker/state-tracker plumbing), not by a caller-supplied field on TraceConfig. Because TraceConfig carries no reexec field at all, the documented reexec uint64 option describes a knob that was either removed or never implemented on this struct, so it has no effect regardless of the type asserted for it.
  • Method: debug_traceTransaction

10. debug_traceTransaction: debug_traceTransaction accepts tracerConfig as raw JSON, not the documented JSON String type

  • Statement: debug_traceTransaction accepts tracerConfig as raw JSON, not the documented JSON String type.
  • URL: https://geth.ethereum.org/docs/interacting-with-geth/rpc/ns-debug
  • Code location:
    • eth/tracers/api.go:971-988
      func (api *API) traceTx(ctx context.Context, tx *types.Transaction, message *core.Message, txctx *Context, vmctx vm.BlockContext, statedb *state.StateDB, config *TraceConfig, precompiles vm.PrecompiledContracts) (interface{}, error) {
      var (
      tracer *Tracer
      err error
      timeout = defaultTraceTimeout
      )
      if config == nil {
      config = &TraceConfig{}
      }
      // Default tracer is the struct logger
      if config.Tracer == nil {
      logger := logger.NewStructLogger(config.Config)
      tracer = &Tracer{
      Hooks: logger.Hooks(),
      GetResult: logger.GetResult,
      Stop: logger.Stop,
      }
      } else {
    • common/types.go:476-486

      go-ethereum/common/types.go

      Lines 476 to 486 in 81ab8b5

      func (d *Decimal) UnmarshalJSON(input []byte) error {
      if !isString(input) {
      return &json.UnmarshalTypeError{Value: "non-string", Type: reflect.TypeFor[uint64]()}
      }
      if i, err := strconv.ParseUint(string(input[1:len(input)-1]), 10, 64); err == nil {
      *d = Decimal(i)
      return nil
      } else {
      return err
      }
      }
  • Description: Root cause — API.traceTx reads config.TracerConfig and passes it straight to the tracer constructor as a json.RawMessage-backed value that the selected tracer unmarshals into its own option struct; the field is declared to hold arbitrary JSON, not a JSON string. Any tracer-specific config object (a map/object literal) is accepted directly, so the documented "JSON String" typing does not describe the field's actual decode behavior.
  • Method: debug_traceTransaction

11. debug_writeMemProfile: The debug_writeMemProfile documentation names debug_writeBlockProfile instead of the implemented WriteMemProfile method

  • Statement: The debug_writeMemProfile documentation names debug_writeBlockProfile instead of the implemented WriteMemProfile method.
  • URL: https://geth.ethereum.org/docs/interacting-with-geth/rpc/ns-debug
  • Code location:
    • internal/debug/api.go:185-187
      func (*HandlerT) WriteMemProfile(file string) error {
      return writeProfile("heap", file)
      }
    • internal/debug/api.go:259-268
      func writeProfile(name, file string) error {
      p := pprof.Lookup(name)
      log.Info("Writing profile records", "count", p.Count(), "type", name, "dump", file)
      f, err := os.Create(expandHome(file))
      if err != nil {
      return err
      }
      defer f.Close()
      return p.WriteTo(f, 0)
      }
  • Description: Root cause — the exported handler for the "heap" pprof profile is HandlerT.WriteMemProfile(file string) error, which calls the shared writeProfile("heap", file) helper; the JSON-RPC method name derived from this handler is debug_writeMemProfile. The documentation section describing this handler is captioned with the unrelated method name debug_writeBlockProfile (which maps to a different handler entirely), so readers following the documented method name will call the wrong endpoint.
  • Method: debug_writeMemProfile

12. debug_dumpBlock / debug_traceBlockByNumber: Methods backed by rpc.BlockNumber reject values above MaxInt64 although their documentation presents the selector as uint64

  • Statement: Methods backed by rpc.BlockNumber reject values above MaxInt64 although their documentation presents the selector as uint64.
  • URL: https://geth.ethereum.org/docs/interacting-with-geth/rpc/ns-debug
  • Code location:
    • eth/api_debug.go:52-69
      func (api *DebugAPI) DumpBlock(blockNr rpc.BlockNumber) (state.Dump, error) {
      opts := &state.DumpConfig{
      OnlyWithAddresses: true,
      Max: AccountRangeMaxResults, // Sanity limit over RPC
      }
      if blockNr == rpc.PendingBlockNumber {
      // If we're dumping the pending state, we need to request
      // both the pending block as well as the pending state from
      // the miner and operate on those
      _, _, stateDb := api.eth.miner.Pending()
      if stateDb == nil {
      return state.Dump{}, errors.New("pending state is not available")
      }
      return stateDb.RawDump(opts), nil
      }
      var header *types.Header
      switch blockNr {
      case rpc.LatestBlockNumber:
    • eth/tracers/api.go:565-582
      func (api *API) traceBlock(ctx context.Context, block *types.Block, config *TraceConfig) ([]*txTraceResult, error) {
      if block.NumberU64() == 0 {
      return nil, errors.New("genesis is not traceable")
      }
      // Prepare base state
      parent, err := api.blockByNumberAndHash(ctx, rpc.BlockNumber(block.NumberU64()-1), block.ParentHash())
      if err != nil {
      return nil, err
      }
      statedb, release, err := api.backend.StateAtBlock(ctx, parent, nil, true, false)
      if err != nil {
      return nil, err
      }
      defer release()
      blockCtx := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil)
      evm := vm.NewEVM(blockCtx, statedb, api.backend.ChainConfig(), vm.Config{})
      defer evm.Release()
  • Description: Root cause — both DebugAPI.DumpBlock(blockNr rpc.BlockNumber) and the block-number entry point feeding API.traceBlock decode their selector through rpc.BlockNumber, which is backed by a signed int64 and whose UnmarshalJSON rejects any decoded value that does not fit in that signed range (so block numbers at or above 2^63 fail decoding entirely, while 2^63-1 is the practical ceiling). The documented Go signatures describe the parameter as an unsigned uint64, which would admit the full unsigned range, so the selector's real capacity is narrower than advertised.
  • Method: debug_dumpBlock, debug_traceBlockByNumber

13. debug_standardTraceBlockToFile: StdTraceConfig uses a value common.Hash for TxHash, not a nullable *common.Hash

  • Statement: StdTraceConfig uses a value common.Hash for TxHash, not a nullable *common.Hash.
  • URL: https://geth.ethereum.org/docs/interacting-with-geth/rpc/ns-debug
  • Code location:
    • eth/tracers/api.go:707-724
      func (api *API) standardTraceBlockToFile(ctx context.Context, block *types.Block, config *StdTraceConfig) ([]string, error) {
      // If we're tracing a single transaction, make sure it's present
      if config != nil && config.TxHash != (common.Hash{}) {
      if !containsTx(block, config.TxHash) {
      return nil, fmt.Errorf("transaction %#x not found in block", config.TxHash)
      }
      }
      if block.NumberU64() == 0 {
      return nil, errors.New("genesis is not traceable")
      }
      parent, err := api.blockByNumberAndHash(ctx, rpc.BlockNumber(block.NumberU64()-1), block.ParentHash())
      if err != nil {
      return nil, err
      }
      statedb, release, err := api.backend.StateAtBlock(ctx, parent, nil, true, false)
      if err != nil {
      return nil, err
      }
    • eth/tracers/api.go:484-490
      func (api *API) StandardTraceBlockToFile(ctx context.Context, hash common.Hash, config *StdTraceConfig) ([]string, error) {
      block, err := api.blockByHash(ctx, hash)
      if err != nil {
      return nil, err
      }
      return api.standardTraceBlockToFile(ctx, block, config)
      }
  • Description: Root cause — standardTraceBlockToFile tests "was a specific transaction requested" with config != nil && config.TxHash != (common.Hash{}), i.e. it compares the field against the zero value of a value-typed common.Hash, not a nil check on a pointer. Because TxHash is declared as a plain (non-pointer) common.Hash on StdTraceConfig, "no transaction specified" and "transaction hash equal to the zero hash" are indistinguishable at the type level, unlike the nullable pointer the documentation implies.
  • Method: debug_standardTraceBlockToFile

14. debug_traceTransaction: debug_traceTransaction returns a dynamic interface result, so custom tracers can return scalar values rather than *ExecutionResult

  • Statement: debug_traceTransaction returns a dynamic interface result, so custom tracers can return scalar values rather than *ExecutionResult.
  • URL: https://geth.ethereum.org/docs/interacting-with-geth/rpc/ns-debug
  • Code location:
    • eth/tracers/api.go:837-854
      func (api *API) TraceTransaction(ctx context.Context, hash common.Hash, config *TraceConfig) (interface{}, error) {
      found, _, blockHash, blockNumber, index := api.backend.GetCanonicalTransaction(hash)
      if !found {
      // Warn in case tx indexer is not done.
      if !api.backend.TxIndexDone() {
      return nil, ethapi.NewTxIndexingError()
      }
      // Only mined txes are supported
      return nil, errTxNotFound
      }
      // It shouldn't happen in practice.
      if blockNumber == 0 {
      return nil, errors.New("genesis is not traceable")
      }
      block, err := api.blockByNumberAndHash(ctx, rpc.BlockNumber(blockNumber), blockHash)
      if err != nil {
      return nil, err
      }
    • eth/tracers/api.go:138-147
      func (api *API) blockByNumberAndHash(ctx context.Context, number rpc.BlockNumber, hash common.Hash) (*types.Block, error) {
      block, err := api.blockByNumber(ctx, number)
      if err != nil {
      return nil, err
      }
      if block.Hash() == hash {
      return block, nil
      }
      return api.blockByHash(ctx, hash)
      }
  • Description: Root cause — API.TraceTransaction is declared to return (interface{}, error), and the concrete value placed in that interface comes from whichever tracer's GetResult() was selected; built-in struct-log tracing returns an *ExecutionResult-shaped value, but custom (e.g. JS/native) tracers are free to return any JSON-marshalable value, including bare scalars or strings, because the RPC layer never asserts a concrete result type. The documented fixed *ExecutionResult return type therefore only describes the default tracer's output, not the endpoint's actual, tracer-dependent contract.
  • Method: debug_traceTransaction

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions