Drt debug
Debug and fix a failing drt sync. Use this skill whenever a user reports errors running drt, asks why a sync is failing, or needs to diagnose drt configuration problems (auth errors, rate limits, template errors, etc.).From its SKILL.md
npx -y skills add drt-hub/drt --skill drt-debugAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
SKILL.md
9.0 KB, as published. Nobody here has run it
Debug a failing drt sync.
Steps
-
Start with the environment-level triage:
drt doctor # checks env vars, profile file, python/extras, common gotchas (v0.7+)This catches the bulk of "it doesn't even start" cases before you read code.
-
If
drt doctoris clean, ask the user to share (or read from context):- The error output from
drt runordrt status - The sync YAML (
syncs/<name>.yml) - The
drt_project.ymlif relevant
- The error output from
-
Reproduce with more signal — pick the right verbosity:
drt run --select <name> --verbose— row-level error detailsdrt run --select <name> --dry-run— config parses, no data sentdrt run --select <name> --dry-run --diff— record-level preview (added/updated/deleted) for queryable destinations (v0.7.1+)drt run --output json/drt status --output json— structured output for CI / scripting (v0.7+)drt run --log-format json— JSON Lines logs to stderr (separate from--output)drt run --select <name> --limit 10— really load ≤10 rows (#774) to reproduce with a small, inspectable send; watermark won't advance (refused formode: mirror/replace)drt run --failed— re-run only the syncs whose last status wasn'tsuccess(#773) — the tight loop while fixing one red sync in a larger projectdrt run --fail-fast— stop after the first failure (#775) instead of running the whole set when the cause is systemic
-
Diagnose the root cause using the patterns below.
-
Suggest a concrete fix with the corrected YAML or command.
Common Error Patterns
Auth errors (401, 403)
- Cause:
token_envorvalue_envenv var not set, or token has wrong permissions. - Fix: Check
echo $MY_TOKEN, verify token scopes. For HubSpot, confirm Private App has CRM write scope. For GitHub, confirmactions: write. - Hardcoded-secret detection (v0.7.5+): if
drt validateflagshardcoded secret detected, the YAML literally contains a token instead oftoken_env. Move it to an env var and reference it.
Rate limit (429)
- Cause: Sending too fast for the destination's limits.
- Fix: Lower
sync.rate_limit.requests_per_second. HubSpot max: 9 req/s. GitHub Actions: 5 req/s. - Also: Add retry config — 429 is retryable by default; in v0.7+ you can also set a per-destination retry override.
- Recover the rows that still failed: enable the dead letter queue (
sync.dlq.enabled: true+on_error: skip, v0.7.9+) so per-record failures persist to.drt/dlq/<sync>.jsonlinstead of being dropped, thendrt retry <sync>re-sends just those once the limit clears.drt statusshows the queue depth.
Connection errors / timeouts
- Cause: Wrong URL, network issue, or destination is down.
- Fix: Verify
urlwithcurl -X POST <url>manually.drt run --dry-runconfirms config parses correctly without sending traffic. - On the source side (#766, v0.8.4+): transient extract failures are already retried automatically (3 attempts, exponential backoff) — a Snowflake
390114token expiry, a Databricks warehouse cold start, a Postgres connection reset, a 5xx on a REST page. No config needed. ⚠️ Retries cover connection + query execution only; a failure after the first row was yielded is not retried (those rows are already loaded and can't be un-sent), so a mid-read drop still fails the sync. Auth failures are never retried — repeated attempts can trip an account lockout, so a persistent connect error is more likely a credential/permission problem than a flaky network.
Template errors
- Cause:
{{ row.field_name }}references a column that doesn't exist in the source. - Fix:
drt run --dry-runpreviews rows; confirm column names match the template. Fordatetime/Decimal/UUIDcolumns flowing into a REST APIbody_template, use thetojson_safefilter (v0.7.6+) rather thanCAST(... AS STRING)in SQL.
Engine-stage attribution (v0.7.6+)
- Cause:
ErrorFormatternow attaches a stage tag to every error (extract/load/finalize/validate). If the surfaced error mentions a stage you didn't expect (e.g. afinalizefailure on a SQL destination's mirror mode), the bug is in that stage's code path — not in the load loop. - Fix: Stage tag narrows the search. For
finalizefailures onsync.mode: mirror(v0.7.7+), checkupsert_keyis set and the source key cardinality is small enough to hold in memory.
Incremental sync not filtering
- Cause:
mode: incrementalset but no saved cursor yet (first run syncs all rows). - Fix: Expected on the first run. Check
drt statusafter first run —last_cursor_valueshould be set. To replay or backfill, usedrt run --cursor-value '<value>' --select <name>(v0.6.2+).
Poisoned or stuck watermark
- Cause: a bad cursor value got persisted (a timezone bug, a bad backfill, a rebuilt source table), so the sync now filters out rows it should send — or sends nothing at all.
- Diagnose:
drt state show <name>— shows the stored watermark, last status and row count. Works for remote watermark backends (GCS / BigQuery) too, which.drt/*.jsoninspection does not. - Fix:
drt run --select <name> --full-refreshre-reads everything and persists a fresh watermark (v0.8.4+). Prefer this over hand-editing.drt/watermarks.json, which does nothing when a remote backend is configured. - Note:
--cursor-valueis a probe — it overrides one run and leaves stored state untouched.--full-refreshis the reset. - Not included:
--full-refreshdoes not reset tracked-mirror state. If mirror deletes look wrong, that isdrt state reset <name> --tracked-mirror— read its warning first, since re-baselining makes rows written by other systems into deletion candidates.
on_error: fail stopping early
- Cause: Default behavior for some destinations — first failure stops the sync.
- Fix: Change to
on_error: skipto continue past failures and see the full error count viadrt run --verboseordrt status. - Recover the failures: pair
on_error: skipwithsync.dlq.enabled: true(v0.7.9+) so the skipped records land in.drt/dlq/<sync>.jsonl;drt retry <sync>replays just them after you fix the root cause. To re-run whole syncs that failed (not individual records), usedrt run --failed(#773). Seedocs/guides/dead-letter-queue.md.
Profile not found
- Cause:
~/.drt/profiles.ymlmissing or profile name mismatch. - Fix:
cat ~/.drt/profiles.yml, verify the profile name matchesdrt_project.yml. Override per-run withdrt run --profile <name>or theDRT_PROFILEenv var.
SQL destinations: qualified-identifier errors (schema.table)
- Cause: Older versions mis-quoted
schema.tableas a single identifier — e.g. ClickHouseCode: 62ondb.table, Postgres syntax errors onmarketing.events. - Fix: Already fixed in v0.7.3 (Postgres
_quote_ident#498), v0.7.4 (MySQL #514), v0.7.8 (ClickHouse #610). Upgrade to the latest patch release and the qualified form parses correctly.
Snowflake mirror mode: MERGE-only write path
- Cause:
sync.mode: mirrorforces the MERGE write path regardless ofconfig.mode(v0.7.7+). - Fix: This is intentional. Don't rely on
replacesemantics for Snowflake mirror — Snowflake's mirror doesn't have a swap path.
Slack / webhook delivery failed (silent)
- Cause: Webhook URL returned 4xx but the upstream sync still succeeded; you missed it.
- Fix: Configure failure alerts (
failure_alertsblock indrt_project.yml, v0.7.0+ via #414) — Slack / webhook notifications fire when any sync run hits a hard failure.
Tools to escalate to
drt test --select <name>— runs the post-sync validation tests (freshness / unique / accepted_values) declared in the sync YAML; use this when "the sync says success but the data looks wrong".drt_run_testMCP tool (v0.7.5+) — same asdrt testbut callable from Claude/Cursor without leaving the chat.- OTel traces (v0.7+, Phase 1+2 shipped) — set
observability.otel.endpointindrt_project.ymlandpip install drt-core[otel]to capture spans. Phase 3 engine spans (drt.sync.run/drt.sync.extract/drt.sync.load) shipped v0.7.10 (pip install drt-core[otel]).
Telemetry
drt ships anonymous opt-in telemetry (PostHog Cloud EU, off by default, v0.7.2+). If a user is debugging in an offline / regulated environment and wants to be sure no analytics fire, set DO_NOT_TRACK=1 or telemetry.enabled: false in ~/.drt/config.yml. drt also honours the DO_NOT_TRACK standard env var with no config.
Reference
docs/llm/CONTEXT.mdfor architecture and key concepts.docs/llm/API_REFERENCE.mdfor all config fields.docs/connectors/for per-connector details (auth, sync modes, gotchas).