Diagnosing Fastify Decorator Undefined Errors in Async Plugins
Learn why Fastify decorators appear undefined in async plugins and how to fix encapsulation, ordering, and timing issues with a step‑by‑step diagnostic guide.
26 Mar 2026, 09:54 UTC

Recognizable Condition
When a route handler or another plugin tries to access fastify.someDecorator you see a ReferenceError or TypeError: Cannot read property 'someDecorator' of undefined. The application starts without error, but the first request that uses the decorator fails.
Cause / Diagnostic Table
| Symptom | Likely Cause | How to Verify |
|---|---|---|
| Decorator undefined error in route | Plugin not wrapped with fastify-plugin (encapsulation boundary) | Check registration: fastify.register(myPlugin) without wrapper |
| Decorator undefined error after async init | Async plugin resolves after routes are bound | Add await fastify.register(asyncPlugin) or ensure routes are registered inside the plugin after await |
| Decorator undefined error despite wrapper | Wrong registration order (consumer before provider) | List fastify.register calls; consumer appears before provider |
Ordered Checks
- Locate the plugin that defines the decorator. Verify it calls
fastify.decorate('someDecorator', value)during its initialization (not inside a route handler). - Confirm wrapping. In your server file, the registration should look like:
If you seeconst fp = require('fastify-plugin'); fastify.register(fp(defineDecoratorPlugin));fastify.register(defineDecoratorPlugin)withoutfp, the encapsulation is the issue. - Check registration order. Ensure the defining plugin is registered before any plugin or route that uses the decorator. Example order:
fastify.register(fp(defineDecoratorPlugin)); fastify.register(fp(consumerPlugin)); fastify.register(fp(routesPlugin)); - Validate async resolution. If the defining plugin is async, make sure you await its registration or register routes inside the plugin after the async work:
fastify.register(async function (fastify, opts) { await someAsyncSetup(); fastify.decorate('someDecorator', result); // routes that use the decorator go here }); - Add temporary logs. Place
console.log('decorator set', fastify.someDecorator)right afterfastify.decorateand anotherconsole.log('decorator used', fastify.someDecorator)at the start of the route. If the first log appears but the second isundefined, the problem is registration order or missing wrapper.
Fixes Tied to Findings
Missing fastify-plugin wrapper
Wrap the plugin definition with fastify-plugin (or use the built‑in dependencies option). This shares the decorated scope with the parent instance.
// before
fastify.register(myDecoratorPlugin);
// after
const fp = require('fastify-plugin');
fastify.register(fp(myDecoratorPlugin));
Incorrect registration order
Move the fastify.register call for the decorator‑defining plugin above any consumer plugins or route plugins.
Async plugin resolves too late
Either await the registration or defer route registration until after the async work:
// Option 1: await registration
await fastify.register(fp(asyncDecoratorPlugin));
// Option 2: register routes inside the plugin after setup
fastify.register(async function (f, opts) {
await init();
f.decorate('someDecorator', value);
f.get('/test', (req, reply) => {
reply.send(f.someDecorator);
});
});
Escalation Criteria
- If after applying the above checks the decorator is still undefined, verify that no other plugin is
fastify.decorate-ing the same name with a different value later in the chain (overwrites can cause confusion). - If the error occurs only under load, check for race conditions where multiple instances of the same plugin are registered; ensure singleton registration.
- When the problem persists across multiple plugins, consider using a dedicated shared context plugin (e.g.,
fastify-pluginthat only holds decorators) and depend on it via thedependenciesarray.
Limitations and Practical Verification
The fastify-plugin wrapper works for both sync and async plugins, but you must still await async registration if you register routes outside the plugin. To verify the fix, start the application and send a request to the route that uses the decorator (e.g., curl http://localhost:3000/test). A successful response with the decorator value confirms visibility; any error indicates the issue remains.
Rollback
Changing the registration order or adding the fastify-plugin wrapper does not alter persistent state. To revert, simply remove the wrapper or restore the previous order; restart the process to see the original behavior.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.