Troubleshooting
Start with the complete error and its Action: line. Skiff distinguishes failures that left the published DuckDB untouched from rare filesystem failures that happened after publication.
Run the basic checks
From the repository:
skiff check
skiff refresh
check validates the complete manifest and file plan, requires each password variable, and connects to every source without loading query data.
No project is found
no skiff.toml found in this directory or its parents
Run Skiff from the data repository or one of its child directories. The file must be named exactly skiff.toml.
A password variable is missing
environment variable REPORTING_DB_PASS is not set
Set the exact variable named by that source's password_env in .env beside
skiff.toml:
REPORTING_DB_PASS='...'
Skiff loads that file automatically. You can instead export the variable through
your shell or process supervisor; an existing process value overrides .env.
If Skiff cannot parse .env, it identifies the file and parse failure. Keep one NAME='value' assignment per line. Blank lines and comments are allowed; quote passwords that contain spaces or special characters.
The source is unreachable
Check the host, port, database, login, password, network path, and certificate chain. A TCP connection times out after 15 seconds.
Use trust_cert = true only for a development server with an intentionally untrusted certificate. Fix certificate trust for production.
SQL Server rejects the login
Skiff reports the reviewed connection fields without printing the password:
error: source 'lcad' is unavailable: SQL Server rejected the login for source 'lcad'.
Connection:
server: datamart.example.com:1433
database: LogisCAD_DwhV8
user: reader
credential: DATAMART_DB_PASS
SQL Server: error 18456, state 1, class 14
insight: SQL Server withheld the specific authentication reason from this client.
Action:
Verify that DATAMART_DB_PASS contains the password for SQL-auth login 'reader'.
Remember that exported values override .env.
If both are correct, ask the DBA to inspect SQL Server error 18456 at the matching time because client state 1 hides the exact reason.
Skiff uses SQL Server authentication. Confirm that the named login exists on the target instance and can access the configured database. If the same credentials fail with SQL Server authentication in another client, the server administrator must inspect the SQL Server error log for the specific 18456 state and reason.
Snapshot mode cannot start
If a source sets snapshot = true, its database must have ALLOW_SNAPSHOT_ISOLATION enabled. Ask the database owner to enable and assess that setting, or remove snapshot mode and schedule the refresh outside write activity. Do not use NOLOCK as a substitute.
A query is rejected before execution
Keep exactly one read-only query in the file. Remove SELECT INTO, write statements, additional statements, and cross-database or linked-server relation names.
An incremental query also cannot have a top-level ORDER BY; remove it because Skiff wraps the query in a watermark filter.
A query exceeds its limit
the query returned more than the 500000 row limit; nothing was published
Narrow the reviewed SQL or raise [limits].max_rows deliberately. Set max_rows = -1 only when the source query is intentionally unbounded; timeout_secs still applies. For timeouts, improve the source query or increase timeout_secs. A limit failure leaves the prior DuckDB untouched.
A check returns rows
A check is a publication gate and passes only when it returns zero rows. Inspect the private-generation invariant represented by the query, correct the source data or project SQL, then rerun the refresh. Do not change the check merely to make it green unless the invariant itself changed.
An incremental append fails after a schema change
Normal and window refreshes append into the previous table shape. Review the query change and downstream compatibility, then rebuild deliberately:
skiff refresh --full
Late rows are missing
A normal incremental refresh loads only keys greater than the stored maximum. Use a bounded backfill that covers the late key:
skiff refresh --since '2026-08-01' --until '2026-08-08'
Use matching integer or timestamp bounds in increasing order.
Schema drift appears
[drift] lines are advisory and do not mean the refresh failed. Review added, removed, or changed columns against downstream queries. The same list appears in the MCP result.
Another refresh holds the lock
Skiff fails immediately and prints the active PID, start time, and output. Let that process finish. If no process owns the reported PID, inspect the host before removing anything; the lock file itself can remain because the operating-system lock, not file existence, controls concurrency.
MCP still uses old SQL
The server freezes the manifest and files at startup. Restart skiff mcp after a reviewed configuration, query, check, or credential-environment change.
The error says changes were made
This indicates the final rename succeeded but syncing the parent directory failed. The new DuckDB may already be published. Inspect the destination and its _skiff_refresh row before deciding whether to run another refresh.