Troubleshooting#

Common issues and how to resolve them.

Validation#

Run edg validate config --config workload.edg to catch errors before hitting the database. Validation errors include line numbers when available:

line 42: duplicate query name "seed_users"

Add --explain for enhanced diagnostics with explanations and correct syntax examples:

edg validate config --config workload.edg --explain

Use edg functions to search for available functions by name:

edg functions ref      # lists all ref_* functions
edg functions seq      # lists all seq_* functions

Config Errors#

“unknown function” or expression compile error#

An arg expression references a function that doesn’t exist. Common causes:

  • Typo in function name: gen('eamil') instead of gen('email'). gofakeit patterns are validated at load time, so a typo produces a clear error.
  • Missing quotes: ref(fetch_users).id instead of ref('fetch_users').id. Dataset names must be quoted strings.
  • Missing init query: Using ref('fetch_users') in run without a corresponding init query named fetch_users.

Run edg validate config --config workload.edg to catch these before hitting the database.

“duplicate query name”#

Two queries in the same config have the same name. Names must be unique across all sections because they’re used as dataset keys for ref_* functions and as metric labels.

“object and args are mutually exclusive”#

A query specifies both object: template_name and args: [...]. Use one or the other.

Connection Errors#

“connecting to database: dial tcp … connection refused”#

The database isn’t running or isn’t reachable at the specified URL. Verify:

  1. The database is running: docker ps or equivalent.
  2. The URL is correct: check host, port, and protocol.
  3. Network access: firewalls, Docker networking (host.docker.internal vs localhost).

“connecting to database: pq: password authentication failed”#

Credentials in the --url are wrong. For local development, check if your database requires a password (CockroachDB insecure mode doesn’t; PostgreSQL usually does).

“driver X requires a license”#

You’re using a pro driver (oracle, mssql, dsql, spanner) without a license. Set --license or export EDG_LICENSE. See Licensing.

Runtime Errors#

High error rate during run#

Check the specific errors with --errors:

edg run --errors --driver pgx --config workload.edg --url ${DATABASE_URL}

Common causes:

  • Foreign key violations: Seed data doesn’t cover the referenced values. Ensure init queries fetch the right dataset.
  • Unique constraint violations: gen() can produce duplicates. Use uuid_v4() or seq_global() for unique columns.
  • Type mismatches: A string value passed where the database expects an integer. Use explicit casts in SQL (e.g. $1::INT) or wrap expressions with int().

Seed is slow#

Batch insert performance depends on size (rows per batch). Too small = too many round trips. Too large = transaction too big.

  • Start with size: 1000 for most databases.
  • Use exec_batch (not exec) for bulk inserts.
  • See Performance Tuning for detailed guidance.

“context deadline exceeded” during seed#

The seed phase is taking longer than expected. Unlike the run phase, seed has no timeout by default. This error usually means the database itself is timing out (e.g. a statement timeout configured on the server).

  • Reduce batch size to commit smaller transactions.
  • Check database-side statement or transaction timeout settings.

“init query X returned no rows”#

An init query came back empty, so the ref() calls reading it have nothing to draw from and the run stops rather than failing on every iteration. Almost always the table it selects from was never seeded:

edg seed --config workload.edg --url "$EDG_URL"

Check the table directly if seeding did run - an interrupted seed, or one capped with --max-rows or --max-duration, can populate earlier tables and leave later ones empty.

Workers not reaching expected QPS#

  • Connection pool exhaustion: Workers may be blocking on connection acquisition. Try increasing --pool-size or reducing --workers. A staged run sizes its pool from the busiest stage, so this normally only bites when --pool-size is set explicitly below that.
  • Database bottleneck: Check database-side metrics (CPU, IOPS, lock contention).
  • Cold cache: Use --warmup-duration to let the database warm up before measuring.

Placeholder Errors#

“pq: could not determine data type of parameter $1”#

This happens when PostgreSQL/CockroachDB can’t infer the type of a bind parameter. Add an explicit cast:

`SELECT * FROM account WHERE id = $1::UUID` (ref('fetch_accounts').id)

Placeholders not working with MySQL / Oracle / MSSQL#

run queries use native bind parameters, which differ by driver:

DriverPlaceholder
pgx, dsql$1, $2
mysql?
oracle:1, :2
mssql, spanner@p1, @p2

Batch queries (exec_batch, query_batch) and batch-expanded queries (using gen_batch, batch, ref_each) inline values into the SQL text, so they always use $1, $2 regardless of driver.

Batch Separator Issues#

Data corruption with comma separators#

Never use commas as batch separators. Generated values (names, addresses) can contain commas, silently splitting one value into multiple rows.

The simplest solution is to use __values__, which avoids batch separators entirely by generating a standard multi-row VALUES clause:

seed_users(count: 1000, size: 100) `INSERT INTO users (name) __values__` (gen('name'))

If you’re using driver-specific batch expansion patterns instead of __values__, always use __sep__ instead of a literal comma. See Driver-Specific Batch Expansion for details.

edg-lang Parse Errors#

“expected identifier, got ‘(’” in .edg files#

In edg-lang format, the opening ( for args must be on the same line as the closing SQL backtick. A newline between the backtick and ( makes the parser think args are a new query name.

Broken - args on a new line:

seed_users(count: 1000, size: 100)
  `INSERT INTO users (id, name) __values__`
  (uuid_v7(), gen('name'))

Fixed - opening ( on same line as backtick (args can then wrap):

seed_users(count: 1000, size: 100) `INSERT INTO users (id, name) __values__` (
  uuid_v7(),
  gen('name')
)

Both the single-line form and the “open on same line, wrap body” form are valid. Only the newline-before-( form breaks.

Editor / LSP Errors#

“couldn’t create connection to server … spawn … ENOENT”#

The VSCode extension couldn’t find the edg binary at the path it tried. It searches edg.path, then PATH, then common install locations (/opt/homebrew/bin, /usr/local/bin, /usr/bin, ~/go/bin, ~/.local/bin, ~/bin, ~/dev/bin).

Check where edg actually lives:

which edg

Then either clear edg.path so the search runs, or point it at the real binary:

{
  "edg.path": "/opt/homebrew/bin/edg"
}

Editors launched from the Finder or Dock don’t inherit a login shell’s PATH, so a binary visible in your terminal may not be visible to the extension. Setting edg.path is the reliable fix in that case.

Expression Debugging#

Use the REPL to test expressions interactively:

edg repl
>> gen('email')
markusmoen@pagac.net

>> uniform.float(0.01, 999.99, 2)
347.82

Load a config to test expressions with globals and reference data:

edg repl --config workload.edg
>> warehouses * 10
10

>> ref('regions').name
eu