A good error message answers three things: what's wrong, why, and how to fix it. Drop any one of the three and the user either repeats the mistake or leaves.
Tone matters as much as content — "Invalid format" blames the user, while "That format doesn't match — try name@example.com" hands them the fix in the same breath. Leaking raw error codes (500, NullPointerException) is the same failure in a different costume.
The demo's ✕ stops at "an error occurred"; ✓ states the exact rule (8+ characters) so the fix is obvious.
When to use
Use this discipline for form validation, API failures, and payment errors. Fill in "why and how" before anything else.