Envio v3 — Inconsistent path resolution between handler:, abi_file_path:, and schema:
Summary
When using a non-default config location (e.g. configs/<variant>/<stem>.yaml rather than <project_root>/config.yaml), Envio v3 silently resolves the three top-level path fields against two different base directories, with no documentation flagging the asymmetry:
| Field |
Base used at resolution |
handler: |
process.cwd() (project root) |
abi_file_path: |
Config file's directory |
schema: |
Config file's directory |
The mismatch is invisible during envio codegen (which loads ABIs + schema but not handlers) and only surfaces at `hen the handler import fails.
Reproduction
Repo layout:
project_root/
├── configs/
│ └── custom/
│ └── base_testnet.yaml
├── src/EventHandlers.ts
├── abis/Foo.json
└── schema.graphql
Config at configs/custom/base_testnet.yaml:
schema: ../../schema.graphql # works
contracts:
- name: Foo
abi_file_path: ../../abis/Foo.json # works
handler: ../../src/EventHandlers.ts # fails at runtime
Run from project_root:
envio codegen --config configs/custom/base_testnet.yaml # passes
envio dev --config configs/custom/base_testnet.yaml # fails
Runtime error (excerpt):
ERROR: Failed to load handler file for contract Foo: ../../src/EventHandlers.ts
Error [ERR_MODULE_NOT_FOUND]: Cannot find module '/src/EventHandlers.ts' imported from
/app/base-template/node_modules/.pnpm/envio@3.0.2_*/node_modules/envio/src/HandlerLoader.res.mjs
../../ walks two levels up from process.cwd() (= project_root) → resolves to /src/EventHandlers.ts at filesy work, the value has to be written as src/EventHandlers.ts (no prefix), even though sibling fields in the same file do require the ../../.
Source of the asymmetry
envio/src/HandlerLoader.res:14-18:
// Paths are resolved relative to process.cwd() (project root)
let toImportUrl = (relativePath: string) => {
let absolutePath =
NodeJs.Path.resolve([NodeJs.Process.cwd(), relativePath])->NodeJs.Path.toString
NodeJs.Url.pathToFileURL(absolutePath)->NodeJs.Url.toString
}
The comment on this function is correct for `handler:` but does not match the actual behavior of `abi_file_path:` / against the config file's directory (verified empirically: moving the config to `/tmp/foo.yaml` causes the schema lookup to land at `/tmp/schema.graphql`, not `<cwd>/schema.graphql`).
Why this is painful
- Silent during codegen. Codegen is the gate users put in CI; it never exercises handler loading. The first failure is at deploy/runtime on the hosted service, which is the worst possible place to discover this.
- Non-default config paths are a first-class feature.
--config <path> / ENVIO_CONFIG exist precisely so users can keep multiple config variants in subdirectories. The current resolution rules make that brittle: any non-flat layout requires the user to maintain two different relative-path conventions in a single YAML file.
- JSON Schema doesn't help. The published
evm.schema.json only types these fields as string; nothing flags the convention difference.
Suggested resolutions
- Unify resolution: resolve all three fields against the config file's directory (intuitive, matches
schema: nately resolve all three against process.cwd(). Either choice is fine as long as it's consistent.
- Validate at codegen time: have
envio codegen perform a dry-resolve of every handler: path so the failure happens at CI time, not at runtime.
Environment
- envio
3.0.2
- Node.js ≥ 22
- Linux container at
/app/base-template/ (hosted service) and macOS local
Envio v3 — Inconsistent path resolution between
handler:,abi_file_path:, andschema:Summary
When using a non-default config location (e.g.
configs/<variant>/<stem>.yamlrather than<project_root>/config.yaml), Envio v3 silently resolves the three top-level path fields against two different base directories, with no documentation flagging the asymmetry:handler:process.cwd()(project root)abi_file_path:schema:The mismatch is invisible during
envio codegen(which loads ABIs + schema but not handlers) and only surfaces at `hen the handler import fails.Reproduction
Repo layout:
Config at
configs/custom/base_testnet.yaml:Run from
project_root:Runtime error (excerpt):
../../walks two levels up fromprocess.cwd()(=project_root) → resolves to/src/EventHandlers.tsat filesy work, the value has to be written assrc/EventHandlers.ts(no prefix), even though sibling fields in the same file do require the../../.Source of the asymmetry
envio/src/HandlerLoader.res:14-18:Why this is painful
--config <path>/ENVIO_CONFIGexist precisely so users can keep multiple config variants in subdirectories. The current resolution rules make that brittle: any non-flat layout requires the user to maintain two different relative-path conventions in a single YAML file.evm.schema.jsononly types these fields asstring; nothing flags the convention difference.Suggested resolutions
schema:nately resolve all three againstprocess.cwd(). Either choice is fine as long as it's consistent.envio codegenperform a dry-resolve of everyhandler:path so the failure happens at CI time, not at runtime.Environment
3.0.2/app/base-template/(hosted service) and macOS local