Error: statement has been finalized
at readDeviceStates (src/lib/database.ts:76:38)
{ code: 'ERR_INVALID_STATE' }
这个错误来自 Node.js 22+ 的内置 node:sqlite 模块,对已经调用了 finalize() 的 StatementSync 对象再次操作时抛出。
原因一:全局缓存 Statement 被意外 finalize
// 错误写法:模块级共享 statement
const readStmt = db.prepare("SELECT * FROM device_states");
export function readDeviceStates() {
return readStmt.all(); // 若 readStmt 被 finalize 则崩溃
}
如果在某处调用了 readStmt.finalize(),后续所有使用都会报错。
原因二:using 关键字自动释放
Node.js SQLite 支持 using 声明(Explicit Resource Management),离开作用域后自动调用 finalize():
// 错误:返回迭代器后 stmt 已被 finalize
function getRows() {
using stmt = db.prepare("SELECT * FROM logs");
return stmt.iterate(); // 离开函数后 stmt 自动 finalize,迭代器失效
}
修复:每次使用时重新 prepare
// 正确写法:每次都 prepare,不缓存
export function readDeviceStates() {
const stmt = db.prepare("SELECT * FROM device_states");
const result = stmt.all();
stmt.finalize();
return result;
}
或者更简洁,不手动调用 finalize(Node.js 会在 GC 时自动释放):
export function readDeviceStates() {
return db.prepare("SELECT * FROM device_states").all();
}
需要性能优化时:用对象包装
如果 prepare 开销较大,可以在模块初始化时准备,但确保 finalize 只在明确不再使用时调用:
class DeviceRepository {
private readStmt: StatementSync;
constructor(private db: DatabaseSync) {
this.readStmt = db.prepare("SELECT * FROM device_states");
}
getAll() {
return this.readStmt.all();
}
close() {
this.readStmt.finalize();
this.db.close();
}
}
生命周期由对象管理,close() 明确释放资源。
ExperimentalWarning 的处理
(node:2436) ExperimentalWarning: SQLite is an experimental feature
node:sqlite 在 Node.js 22 中是实验性功能,可以用以下方式消除警告:
node --no-experimental-warnings server.js
或在代码中:
process.removeAllListeners('warning');
生产环境建议关注 Node.js 版本更新,等待 SQLite 模块稳定。
