Skip to content

Commit

Permalink
fix(pg): display error from child process in simulation
Browse files Browse the repository at this point in the history
  • Loading branch information
sam-goldman committed Jan 10, 2024
1 parent 6ec9b6d commit 15b9a25
Show file tree
Hide file tree
Showing 3 changed files with 38 additions and 7 deletions.
5 changes: 5 additions & 0 deletions .changeset/stupid-mugs-exercise.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@sphinx-labs/plugins': patch
---

Display error from child process in simulation
12 changes: 9 additions & 3 deletions packages/plugins/src/hardhat/hardhatRunner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,15 @@ const runHardhatSimulation = async (
process.stdout.write(JSON.stringify({ receipts, batches }))
}

// This display errors in a coherent stack trace. The default behavior displays the stack
// trace twice, where one is a stringified version that's difficult to read.
// If an error occurs, we write the error message and stack trace to `stdout` then exit the process
// with exit code `1`. We write the error to `stdout` instead of `stderr` because `stderr` may
// contain warnings that were written via `console.warn`, which are indistinguishable from the
// actual error message in `stderr`. By using `stdout`, we can throw an error that doesn't contain
// warnings in the parent process.
process.on('uncaughtException', (error) => {
console.error(error)
process.stdout.write(
JSON.stringify({ message: error.message, stack: error.stack })
)

process.exit(1)
})
28 changes: 24 additions & 4 deletions packages/plugins/src/hardhat/simulate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ export const simulate = async (
// (although unlikely) that this same situation could happen in a user's test suite. We resolve
// this by creating the Hardhat node in a child process via `spawnAsync`. This child process exits
// when `spawnAsync` returns.
const { stdout, stderr, code } = await spawnAsync(
const { stdout, code } = await spawnAsync(
'node',
[hardhatRunnerPath],
envVars,
Expand All @@ -161,9 +161,29 @@ export const simulate = async (

if (code !== 0) {
const networkName = getNetworkNameForChainId(BigInt(chainId))
throw new Error(
`Simulation failed for ${networkName} at block number ${block.number}. Reason:\n${stderr}`
)
let errorMessage: string = `Simulation failed for ${networkName} at block number ${block.number}.`
try {
// Attempt to decode the error message. This try-statement could theoretically throw an error
// if `stdout` isn't a valid JSON string.
const error = JSON.parse(stdout)

// If the stack trace includes the error message, we only use the stack trace so that we don't
// display the error reason twice.
if (
typeof error.stack === 'string' &&
error.stack.includes(error.message)
) {
errorMessage += `\n\n${error.stack}`
} else {
// Display both the error message and the stack trace.
errorMessage += `\n\n${error.message}\n\n${error.stack}`
}
} catch {
// An error occurred while attempting to decode `stdout` into an error message. We'll display
// the raw `stdout` to the user in case it's useful.
errorMessage += `\n\n${stdout}`
}
throw new Error(errorMessage)
}

const receipts = JSON.parse(stdout).receipts
Expand Down

0 comments on commit 15b9a25

Please sign in to comment.