Debug failed materialize
Diagnose per-cell failures from `folio materialize` — read the §10.6 envelope, locate the bad cell in `provenance.jsonl`, narrow with a positional target + `--ids`, and re-run with `--force`. Invoke when the user reports "materialize is failing", "this column came back null", "AI calls timing out", or pastes a non-empty `failures[]` list from the envelope.From its SKILL.md
npx -y skills add nyuta01/folio --skill debug-failed-materializeAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
7.2 KB, ~1.9k tokens by cl100k_base, as published. Nobody here has run it
Debug a failed folio materialize
folio materialize exits 0 even on partial failures — bad cells
are reported in the envelope's failures[]. This skill walks an
agent through reading that envelope, narrowing scope to the offending
cell(s), and re-running until failures is empty.
When this skill applies
- The user pasted a
materializeenvelope wherefailuresis non-empty. - The user says a derived column "came back null" or "didn't update".
- The user says materialize is "stuck", "slow", or "running forever".
- A CI gate failed because
failures | length > 0.
This skill does not apply when:
- The user has a contract error (
folio validatefails). That's a schema problem, not a materialize problem — fixcontract.yamlfirst. - The cells succeeded but the values are wrong. That's a prompt /
script bug — narrow with the positional target +
--ids+--forceto iterate, but it's not a "failure" in the envelope sense.
Recap: the §10.6 envelope
Every folio materialize run prints:
{
"materialized": 12,
"skipped": 7,
"failures": [
{"record_id":"cust_006","field":"industry_tag",
"error":"...","error_type":"FolioError"}
],
"total_cost": 0.0034
}
| Field | Meaning |
|---|---|
materialized | Cells written this run. |
skipped | Cells avoided (cache hit, respect_human_override, no foreign match for cross_sheet, etc.). |
failures | Per-record × field errors. Other records keep going. |
total_cost | Sum of cost_usd from ai calls. |
A non-zero exit code is not how partial failures are signalled.
failures: [] is the only success criterion.
Procedure
-
Capture the envelope and pretty-print it.
out=$(folio materialize ./<sheet> --actor agent:debug) echo "$out" | jqIf
failuresis empty, the run is healthy — the user's complaint is probably about values, not failures. Jump to step 6. -
Group failures by
(field, error_type)to see if it's one bug or many:echo "$out" | jq '[.failures[] | {field, error_type}] | group_by(.) | map({key: (.[0].field + ":" + .[0].error_type), count: length})'- All failures share
(field, error_type)→ one bug; pick anyrecord_idto reproduce. - Different
(field, error_type)pairs → multiple bugs; treat each group independently.
- All failures share
-
Narrow the next run to one cell. Pick the smallest reproducible case from step 2 — one target, one record:
folio materialize ./<sheet> <field> \ --actor agent:debug \ --ids <record_id> \ --forcefolio materializetakes the target as a positional argument (one at a time);--idsis comma-separated or repeatable.--forceignores the cache so you re-execute even if the input hasn't changed. Without it, after the first failure the cell may still cache-miss but you risk wasted "skipped" runs while iterating. -
Read the
erroranderror_type. The pattern that diagnoses most failures:error_type(typical)What it usually means FolioError— "script exited with N"Python derivation crashed. Run the script directly with the row's input JSON to repro. FolioError— "AIClient call failed"Provider timeout / 5xx / rate limit. Check ANTHROPIC_API_KEY, retry; consider raisingmaterialization.retries.FolioError— "missing key in output_schema"LLM returned JSON missing a required key. Tighten the prompt or the schema. FolioError— "no rows in foreign sheet"Misconfigured cross_sheetsource_sheetpath. Check it's relative to the calling sheet, not toderivations/.FolioError— "primary key not unique"The sheet itself is broken; this is upstream of materialize. Run folio validate. -
Inspect provenance to confirm the fix. After a successful re-run, the cell should have a fresh provenance line:
folio provenance ./<sheet> <record_id> <field>folio provenancetakes the record ID and field as positional arguments. Add--historyto see every entry in the append-only log instead of just the latest.You should see a new
at:timestamp, the matchingactor:, and foraicells a populatedmodel:andcost_usd:(unless the model is unknown to the price table — that'snullby design). -
(If
failureswas empty but values look wrong.) This is a logic bug, not a failure:- Read the cell's provenance line — what
sourceproduced it (ai,python,cross_sheet,human)? - If
source: human, the cell was edited andrespect_human_override(defaulttrue) is preserving the edit. Use--forceor unset the override on the derivation. - If
source: cross_sheetand the value isnull, there was no foreign match — that's silent by design (see theadd-derivation-cross-sheetskill). - Otherwise, fix the derivation (prompt / script / schema) and
repeat from step 3 with
--force.
- Read the cell's provenance line — what
-
Once the one-record case is healthy, broaden.
folio materialize ./<sheet> <field> --actor agent:debugIf
failuresis[], drop the positional target and run everything.
Verify
folio materialize ./<sheet> --actor agent:debug
echo $? ; echo "$last" | jq '.failures | length'
Exit code is 0 and failures | length == 0. To turn this into a
CI gate:
out=$(folio materialize ./<sheet> --actor agent:ci)
echo "$out"
[[ "$(echo "$out" | jq '.failures | length')" == "0" ]] || exit 1
Common mistakes (don't make them)
- Trusting exit code alone.
folio materializeexits 0 with a non-emptyfailures[]. Always inspect the envelope. - Re-running without
--forcewhile debugging. The cache may hide your fix attempts as "skipped". Use--forceuntil the cell goes green, then drop it. - Conflating "skipped" with "failed".
skippedincludes cache hits,respect_human_overrideskips, and "no match" forcross_sheet— all benign. The only red signal isfailures[]. - Editing a derivation file mid-debug and forgetting it invalidates
the cache. That's correct behaviour, but it means the next run
will re-execute many cells. Narrow with a positional target +
--idsduring iteration. - Filing a bug against Folio for a
pythonscript crash. Theerrorfield reproduces the user's script's exception verbatim; the bug is in their derivation, not in Folio. Repro by running the script directly with the input JSON.
See also
- The
add-derivation-aiandadd-derivation-cross-sheetskills for authoring the YAML in the first place. folio status <sheet>for a roll-up of which derived fields are derived vs human vs missing across the whole sheet.
What ships with it
Read from the repository
Just SKILL.md. No reference files, no scripts.