WordPress has thousands of hooks, spread across core, bundled themes, and every plugin you’ve ever installed. An AI coding agent has seen an enormous amount of WordPress code during training, which makes it very good at producing code that looks right. It also makes it capable of confidently generating a hook or function name that sounds exactly like something WordPress would have, and doesn’t actually exist. This lesson is about recognizing that failure mode and building the habit that catches it before it ships.
A local Studio site with WordPress core files on disk (from Lesson 1), and at least one AI coding agent connected from Lesson 2. No new setup, this lesson is entirely about a habit, not a tool.
Step 1: understand why this happens
A large language model generates code one token at a time, choosing each next token
based on what’s statistically likely given everything before it. When you ask it to
“run some code when a post is published,” it has seen publish_post and countless
other verb_noun-shaped WordPress hook names during training. If the exact hook it
needs doesn’t come to mind clearly, the model can still produce something in the same
shape, wp_after_post_published or on_post_status_publish, that reads as completely
plausible and is simply not a real WordPress hook.
This isn’t a WordPress-specific bug in the model. It’s a general property of how
these agents generate code for any large, hook-heavy, loosely-documented-in-training-data
API surface. WordPress happens to be an especially good candidate for it: a huge hook
surface, many near-synonymous naming patterns across its history (save_post versus
wp_insert_post versus post_updated), and enough plugin-specific custom hooks in
the training data that the line between “real WordPress core hook” and “a hook some
plugin invented” gets blurry for a model that’s seen both equally often.
Step 2: recognize the pattern in generated code
Here’s the kind of thing an agent might hand you when asked to hook into post publishing, sanitize slug changes:
// File: my-plugin.php
// This looks entirely reasonable and does not work
add_action( 'wp_post_slug_changed', function ( $post_id, $old_slug, $new_slug ) {
// ...
}, 10, 3 );
wp_post_slug_changed does not exist in WordPress core. The real mechanism for
reacting to a slug change involves comparing post_name values inside the standard
save_post or post_updated hooks yourself, there’s no single hook that hands you
old and new slugs directly. The generated code will run with no errors and no effect,
which is the genuinely dangerous version of this failure: a hallucinated hook that
throws a fatal error gets caught immediately, one that silently never fires gets
shipped.
Step 3: verify against a real source, every time
Two reliable ways to check whether a hook or function actually exists, use both, not just the one that’s most convenient:
# Context: run against your local Studio site's wp-includes and wp-admin directories
grep -r "do_action( 'wp_post_slug_changed'" /path/to/studio-site/wp-content/../wp-includes/
grep -r "do_action( 'wp_post_slug_changed'" /path/to/studio-site/wp-admin/
# No matches means the hook doesn't exist in core.
# Compare against a hook you know is real, as a sanity check:
grep -r "do_action( 'save_post'" /path/to/studio-site/wp-includes/
If grep comes back empty for the hook the agent gave you, and comes back with real matches for a hook you already know exists, that’s your confirmation. This is exactly why Lesson 1 has you running a local Studio site with real core files on disk rather than working purely from an agent’s memory of WordPress: you have ground truth sitting right next to the generated code.
Step 4: build the check into how you prompt, not just how you review
The most reliable fix isn’t reviewing harder after the fact, it’s asking the agent to verify as part of generating the code in the first place. Since Claude Code and Cursor’s agent mode can both run shell commands, you can ask directly:
Before writing this hook, grep the WordPress core files in this project
for the hook name you're planning to use, and confirm it exists before
you write any code against it. If it doesn't exist, tell me and suggest
the real hook that gets closest to what I need.
This turns an unreliable “trust the model’s training data” step into a reliable “trust the tool call against real files” step, the same principle Lesson 5 in this course builds on for context management generally: an agent with access to ground truth is far more reliable than one working from memory alone.
Test it
Ask your coding agent to add functionality to a plugin using a hook you’re not completely sure exists, phrase it vaguely enough to invite a guess (“run some code right after a post’s slug changes”), and see what it produces. Then run the grep check yourself against your local core files, regardless of how confident the generated code looks.
An AI coding agent will present a hallucinated hook with exactly the same tone and certainty as a real one. There’s no hedging language to watch for, “this hook fires when…” reads identically whether the hook is real or invented. Verification against developer.wordpress.org or core source is the only thing that actually tells you the difference, not how the explanation is worded.
Recap
AI coding agents hallucinate WordPress hooks and function names because they generate the most statistically plausible next code, not necessarily the most factually correct one, and WordPress’s huge, historically inconsistent hook surface makes this an easy trap. The fix is cheap and mechanical: check developer.wordpress.org, grep WordPress core directly against your local Studio site, and build that verification step into your prompts rather than relying on catching it in review. The next lessons in this module turn that discipline into reusable guardrails instead of a manual habit.
Resources & further reading
- Function reference, developer.wordpress.org
- Hooks reference, developer.wordpress.org
- WordPress core Trac, core.trac.wordpress.org