Reading Validation Errors
When a block or stack definition is wrong, Stackie’s job is to tell you exactly what’s wrong, where it is, and how to fix it. The same diagnostic pipeline drives every surface — CLI, MCP, and dashboard — so the answer to “what broke?” is consistent no matter where you’re looking.
The anatomy of a diagnostic
Every validation error Stackie emits has the same fields:
code— a stable identifier likeE001, useful for searching docs and filing issuesseverity—"error"or"warning"message— one sentence explaining what’s wrongsource_file— workspace-relative path of the offending file (Stackie never emits absolute host paths over the wire)span—{ start_line, start_col, end_line, end_col }, all 1-indexedhint— short remediation advice when Stackie can suggest oneyaml_excerpt— up to ±5 lines of YAML around the span, ready to render
These fields render differently per surface but always carry the same payload.
CLI
Run the linter directly:
stackie blocks lint ~/.stackie/sources/
A failing block produces output like:
error[block.missing-command]: block must define `command`
--> ~/.stackie/sources/local/echo.yml:1:1
|
1 | echo:
| ^^^^ block defined here
2 | description: "Echoes a string"
|
= help: add `command: "<the command to run>"` under the block name
The carat underline points to the source span. The help: line is a
suggested fix — copy-pasteable when possible.
If you ran the linter against a directory, every failing file gets its own diagnostic block, followed by a summary:
2 errors in 1 file (5 files checked)
Exit code is 0 on success and 1 on any error, so the command works in
pre-commit hooks and CI gates without wrapping.
Dashboard
The daemon validates blocks and stacks as you start them, and any failure
is recorded as a notification. The dashboard’s top bar shows an unread count;
the system-tray menu mirrors the same number in its “View Dashboard” entry,
so you’ll see View Dashboard (🔔 1) when something needs your attention
even before you open the UI.
On macOS the tray label includes the bell glyph — View Dashboard (🔔 1).
On Linux and Windows the bell is dropped because emoji rendering in tray
menus is inconsistent across desktop environments, and the label shows
View Dashboard (1) instead. The number is the same on every platform.
Clicking a notification deep-links into the Definition tab of the affected stack or block. The Definition tab shows:
- The original YAML — Stackie de-serialises the in-memory model back to YAML so you see the exact file it ran with, not a re-rendered subset.
- The diagnostic span — the same excerpt the CLI prints, rendered with syntax highlighting and a red wavy underline on the offending tokens.
- The full message and code — clickable to copy.
- The fix suggestion — when the diagnostic produces one.
If a stack failed because one of its blocks was malformed, the stack’s notification links you to the block’s Definition tab — the chain of “why did this break?” leads to the actual root cause in one click instead of making you trace it manually.
MCP
Agents and editor integrations call the validate_block MCP tool. It takes a
single argument — a path to a file or directory — and returns a JSON envelope:
{
"status": "invalid",
"diagnostics": [
{
"code": "E001",
"severity": "error",
"message": "block must define `command`",
"source_file": "./sources/local/echo.yml",
"span": {
"start_line": 1,
"start_col": 1,
"end_line": 1,
"end_col": 5
},
"hint": "add `command: \"<the command to run>\"` under the block name",
"yaml_excerpt": "echo:\n description: \"Echoes a string\"\n"
}
]
}
status is "invalid" whenever any diagnostic in the array has
severity == "error"; warnings alone leave status as "valid". The
diagnostics array is empty when the file passes.
source_file is workspace-relative — Stackie redacts absolute host paths
before sending them over MCP, so you’ll never see /Users/you/... or
C:\Users\you\... in the response. Directories you pass that sit outside
the workspace get the bare filename only.
span, hint, and yaml_excerpt are optional — they’re omitted when
Stackie can’t compute them. code, severity, and message are always
present.
The field names match the CLI and dashboard payloads exactly — if you’ve seen one, you’ve seen them all.
Notification retention
Notifications are kept for 7 days by default and then purged
automatically. Adjust this in ~/.stackie/settings.yml:
notifications:
retention_days: 30
The daemon reads this value at boot and applies it for its lifetime — edit
the file and restart the daemon to change retention. Setting
retention_days: 0 does not disable the purge; it makes every
notification eligible for deletion on the next purge tick (within an hour),
which is effectively “do not retain”. To pause purging, raise the value
high (e.g. 36500 for ~100 years) — the schema accepts up to 3650 days.
When to file a bug
If the diagnostic message is wrong, misleading, or points at the wrong
span, that’s a bug in Stackie — file it at
github.com/stackie-dev/stackie
with the offending YAML and the diagnostic output. Including the code:
field in your report makes triage instant.