Metabase serdes-yaml-edit
Edit Metabase serdes YAML files (cards, dashboards, databases) with correct portable references and structural conventions. Use when modifying exported YAML content.
git clone https://github.com/metabase/metabase
T=$(mktemp -d) && git clone --depth=1 https://github.com/metabase/metabase "$T" && mkdir -p ~/.claude/skills && cp -r "$T/.claude/skills/serdes-yaml-edit" ~/.claude/skills/metabase-metabase-serdes-yaml-edit && rm -rf "$T"
.claude/skills/serdes-yaml-edit/SKILL.mdSerdes YAML Edit Skill
Golden Rule
Run both checkers after every edit. No exceptions. Do not batch multiple edits before validating.
clojure -M:run:ee --mode checker --checker structural --export /path/to/export-dir clojure -M:run:ee --mode checker --checker cards --export /path/to/export-dir
If either checker fails, fix the issue before making further edits.
Portable References
Serdes YAML uses portable references instead of integer IDs. This is the most important concept for editing.
Database references
String name:
"Sample Database"
Table references
Array of
[database, schema, table]:
table_id: - Sample Database - PUBLIC - ACCOUNTS
Field references
Array of
[database, schema, table, field]:
id: - Sample Database - PUBLIC - ACCOUNTS - EMAIL
Field refs in queries
field_ref: - field - - Sample Database - PUBLIC - ACCOUNTS - EMAIL - null
The outer array is
[field, <field-path>, <options>]. The options are usually null.
Card references
Entity ID string (21 characters):
"Qk5TgsNx4ubXIUtsQmT8G"
Source table in queries
dataset_query: database: Sample Database query: source-table: - Sample Database - PUBLIC - ACCOUNTS type: query
Safe Edits (low risk)
These rarely break validation:
- card/dashboard display namename
- card/dashboard descriptiondescription
- visualization type (table, bar, line, pie, etc.)display
- chart configurationvisualization_settings
- true/falsearchived
- move to a different collection (use entity_id of target collection, or null for root)collection_id
Structural Edits (must match schema)
These must use valid portable refs and will be caught by the cards checker if wrong:
- the query definitiondataset_query
- column metadata (must match the query's output columns)result_metadata
- must reference a table that exists in the exporttable_id
- must reference a database that exists in the exportdatabase_id
Editing result_metadata
Each entry in
result_metadata describes an output column. When changing a query's source table or fields, you must update result_metadata to match. Each field entry needs at minimum:
- column name (e.g.,name
)EMAIL
- Metabase type (e.g.,base_type
,type/Text
,type/Integer
)type/DateTime
- human-readable namedisplay_name
- portable field referencefield_ref
- portable field pathid
- portable table pathtable_id
- usuallysourcefields
Common Operations
Rename a card
Change
name: at the top level. Safe, no ref changes needed.
Change a card's source table
Update all of these consistently:
- top-leveltable_id
- in the querydataset_query.query.source-table
- every field entry'sresult_metadata
,id
, andfield_reftable_id
Add a filter to a structured query
dataset_query: database: Sample Database query: source-table: - Sample Database - PUBLIC - ORDERS filter: - ">" - - field - - Sample Database - PUBLIC - ORDERS - TOTAL - null - 100 type: query
Change visualization type
display: bar # was: table
Valid types:
table, bar, line, pie, scalar, row, area, combo, scatter, funnel, map, pivot, progress, gauge, waterfall
Looking Up Valid References
The export directory IS the reference catalog. When you need to find the correct name for a database, table, or field, look it up directly from the export.
List valid databases
ls databases/
Each entry is a database name (directory for serdes format, .yaml for concise format).
List valid tables for a database
ls databases/<db-name>/schemas/<schema>/tables/
Example:
ls databases/Sample\ Database/schemas/PUBLIC/tables/ shows ACCOUNTS, ORDERS, PRODUCTS, etc.
List valid fields for a table
ls databases/<db-name>/schemas/<schema>/tables/<table>/fields/
Example:
ls databases/Sample\ Database/schemas/PUBLIC/tables/PRODUCTS/fields/ shows CATEGORY.yaml, TITLE.yaml, PRICE.yaml, etc. The filename (minus .yaml) is the field name.
Find card entity IDs
Card filenames encode the entity ID:
<entity-id>_<slug>.yaml. The entity ID is the part before the first underscore (21 characters).
Self-Healing Loop
When a checker reports errors, follow this loop:
- Read the error message - it tells you what's wrong and often suggests the fix
- Look up the correct value from the export directory (see "Looking Up Valid References" above)
- Fix the YAML
- Re-run both checkers
- Repeat until clean
Do not guess at fixes. Always look up the correct value from the export.
Understanding Checker Errors
Structural checker errors
The structural checker validates YAML shape against Malli schemas. Common errors:
Missing required key with typo suggestion:
Missing required key 'name' - found 'nameee' which may be a typo
Fix: rename the typo'd key back to the correct name. The checker tells you what it expected and what it found.
Wrong type:
'archived' should be a boolean, got: "yes"
Fix: use
true/false, not strings.
Unknown key:
Unknown key 'foobar' in card
Fix: remove the key, or check if it's a typo of a known key.
Cards checker errors
The cards checker validates that queries resolve against exported metadata. Error types:
UNRESOLVED REFERENCES:
UNRESOLVED REFERENCES: - field: Sample Database.PUBLIC.PRODUCTS.CATEGORYYY
The dotted path tells you exactly which reference failed. Look up the correct value:
- Last segment is the field name - check
databases/.../tables/<table>/fields/ - Third segment is the table name - check
databases/.../schemas/<schema>/tables/ - First segment is the database name - check
databases/
ERROR (query construction failed):
ERROR: Error creating query from legacy query: Invalid output: ...
Usually means the query structure is malformed. This often follows unresolved references - fix the refs first, then re-check.
ERROR (nil name):
ERROR: Invalid output: {:name ["should be a string, got: nil"]}
The card is missing its
name field (likely a structural issue that also affects the cards checker).
Exit codes
- Exit 0: all checks passed
- Exit 1: one or more failures
File Naming Convention
Card files:
collections/<collection-path>/cards/<entity-id>_<slug>.yaml
The filename slug should match the card name (lowercase, underscored). If you rename a card, consider renaming the file to match, though import works on entity_id not filename.
What NOT to Edit
- this is the identity of the object, changing it creates a new object on importentity_id
- internal serdes metadata, leave it aloneserdes/meta
- timestamp, no reason to changecreated_at
- email reference, leave itcreator_id
- informational, leave itmetabase_version