Skip to content

Envio v3 — Inconsistent path resolution between handler:, abi_file_path:, and schema: #1261

Description

@gauthiermyr

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

  1. 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.
  2. 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.
  3. JSON Schema doesn't help. The published evm.schema.json only types these fields as string; nothing flags the convention difference.

Suggested resolutions

  1. 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.
  2. 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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions