Fastify-React应用配置环境变量时出现AVV_ERR_PLUGIN_NOT_VALID错误求助
问题:Fastify中配置环境变量触发AVV_ERR_PLUGIN_NOT_VALID错误
在Fastify-React应用中配置环境变量时,运行代码触发了AVV_ERR_PLUGIN_NOT_VALID错误,请求分析原因并解决。
代码示例
const fastify = require('fastify')({ logger: { level: 'debug', } }); console.log('hello from server.js 1 !'); // setting up cors: const cors = require("@fastify/cors"); fastify.register(cors, { origin: "*" }); // setting up environment variables const fastifyEnv = require('@fastify/env'); const envSchema = { type: 'object', required: ['YOUTUBE_DATA_API_KEY', 'TESTVAR', 'GOOGLE_CLIENT_ID', 'GOOGLE_CLIENT_SECRET', 'JWT_SECRET'], properties: { YOUTUBE_DATA_API_KEY: { type: 'string' }, TESTVAR: { type: 'string' }, GOOGLE_CLIENT_ID: { type: 'string' }, GOOGLE_CLIENT_SECRET: { type: 'string' }, JWT_SECRET: { type: 'string' } } }; const options = { confKey: 'config', schema: envSchema, dotenv: { path: `${__dirname}/.env`, debug: true } }; fastify.register(fastifyEnv, options); fastify.ready(err => { if (err) throw err; console.log('fastify.config at server.js: ', fastify.config); const fastifyJwt = require('@fastify/jwt'); fastify.register(fastifyJwt, { secret: fastify.config.JWT_SECRET }); }); // setting up authentication: fastify.decorate('verifyJWTandLevel', (request, reply, done) => { done(); }).decorate('verifyUserAndPassword', (request, reply, done) => { done(); }).register(require('@fastify/auth')).after(() => { fastify.route({ method: 'POST', url: '/auth-multiple', preHandler: fastify.auth([fastify.verifyJWTandLevel, fastify.verifyUserAndPassword]), handler: (req, reply) => { req.log.info('Auth route'); reply.send({ hello: 'world' }); } }); }); // other setup... const videoRouter = require('./src/routes/videoRouter'); fastify.register(videoRouter, { prefix: '/api/v1' }); fastify.get('/', (request, reply) => { console.log('hello from server.js 5000!'); reply.send({ hello: 'fastify server at 5000!' }); }); // Start function const start = async () => { try { await fastify.listen({ port: 5000, address: '0.0.0.0' }); console.log('Server is running on port 5000'); } catch (err) { fastify.log.error(err); process.exit(1); } }; fastify.ready(err => { if (err) throw err; start(); });
错误栈信息
[FASTIFY] AvvioError [Error]: Root plugin has already booted [FASTIFY] at Boot._addPlugin (/home/totoro/vc_app/vc-app/node_modules/avvio/boot.js:244:11) [FASTIFY] at Boot.use (/home/totoro/vc_app/vc-app/node_modules/avvio/boot.js:216:25) [FASTIFY] at server. [as register] (/home/totoro/vc_app/vc-app/node_modules/avvio/boot.js:40:14) [FASTIFY] at /home/totoro/vc_app/vc-app/backend/server.js:42:13 [FASTIFY] at manageErr (/home/totoro/vc_app/vc-app/node_modules/fastify/fastify.js:595:11) [FASTIFY] at exit (/home/totoro/vc_app/vc-app/node_modules/fastify/lib/hooks.js:110:5) [FASTIFY] at manageTimeout (/home/totoro/vc_app/vc-app/node_modules/fastify/lib/hooks.js:127:11) [FASTIFY] at _encapsulateThreeParam (/home/totoro/vc_app/vc-app/node_modules/avvio/boot.js:562:7) [FASTIFY] at Boot.timeoutCall (/home/totoro/vc_app/vc-app/node_modules/avvio/boot.js:458:5) [FASTIFY] at Boot.callWithCbOrNextTick (/home/totoro/vc_app/vc-app/node_modules/avvio/boot.js:440:19) { [FASTIFY] code: 'AVV_ERR_PLUGIN_NOT_VALID'
原因与解决方案
错误原因
问题出在**fastify.ready回调中调用fastify.register(fastifyJwt)**。Fastify的ready事件是在所有插件加载、实例启动完成后触发的,此时根插件已经进入"booted"状态,无法再注册新插件,这就是AVV_ERR_PLUGIN_NOT_VALID错误的核心原因。
Fastify要求所有插件注册操作必须在实例启动前完成,ready回调只能用来执行启动后的初始化逻辑,不能添加新插件。
解决方案
将fastifyJwt的注册移到ready回调之外,利用Fastify的after钩子,确保在fastifyEnv加载完成后再注册fastifyJwt。
修改后的完整代码
const fastify = require('fastify')({ logger: { level: 'debug', } }); console.log('hello from server.js 1 !'); // setting up cors: const cors = require("@fastify/cors"); fastify.register(cors, { origin: "*" }); // setting up environment variables const fastifyEnv = require('@fastify/env'); const envSchema = { type: 'object', required: ['YOUTUBE_DATA_API_KEY', 'TESTVAR', 'GOOGLE_CLIENT_ID', 'GOOGLE_CLIENT_SECRET', 'JWT_SECRET'], properties: { YOUTUBE_DATA_API_KEY: { type: 'string' }, TESTVAR: { type: 'string' }, GOOGLE_CLIENT_ID: { type: 'string' }, GOOGLE_CLIENT_SECRET: { type: 'string' }, JWT_SECRET: { type: 'string' } } }; const options = { confKey: 'config', schema: envSchema, dotenv: { path: `${__dirname}/.env`, debug: true } }; // 注册fastifyEnv后,用after钩子确保环境变量加载完成再注册fastifyJwt fastify.register(fastifyEnv, options).after(() => { const fastifyJwt = require('@fastify/jwt'); fastify.register(fastifyJwt, { secret: fastify.config.JWT_SECRET }); }); // setting up authentication: fastify.decorate('verifyJWTandLevel', (request, reply, done) => { done(); }).decorate('verifyUserAndPassword', (request, reply, done) => { done(); }).register(require('@fastify/auth')).after(() => { fastify.route({ method: 'POST', url: '/auth-multiple', preHandler: fastify.auth([fastify.verifyJWTandLevel, fastify.verifyUserAndPassword]), handler: (req, reply) => { req.log.info('Auth route'); reply.send({ hello: 'world' }); } }); }); // other setup... const videoRouter = require('./src/routes/videoRouter'); fastify.register(videoRouter, { prefix: '/api/v1' }); fastify.get('/', (request, reply) => { console.log('hello from server.js 5000!'); reply.send({ hello: 'fastify server at 5000!' }); }); // Start function const start = async () => { try { await fastify.listen({ port: 5000, address: '0.0.0.0' }); console.log('Server is running on port 5000'); } catch (err) { fastify.log.error(err); process.exit(1); } }; // 直接用listen启动,listen内部会自动等待所有插件加载完成 start();
关键修改点
- 移除了第一个
fastify.ready回调,改用fastify.register(fastifyEnv, options).after()钩子,确保fastifyEnv加载完成后再注册fastifyJwt,此时环境变量已可用。 - 移除了第二个
fastify.ready回调,直接调用start()函数,因为fastify.listen()内部会自动处理插件加载完成的逻辑,无需手动监听ready事件。
内容的提问来源于stack exchange,提问作者Toto
相关产品推荐
相关产品推荐

