agalwood/Motrix · error · MetadataError
plugin.metadata.quota_exceeded
plugin.metadata.quota_exceeded
Error message
plugin.metadata.quota_exceeded: projected usage ${projected} exceeds quota ${this.perPluginPerTaskBytes} What it means
Before writing, projected total size for this (task, plugin) is computed as currentTotal - existingSizeOfKey + newSizeOfKey and compared against perPluginPerTaskBytes (default 64 KB). The error fires on the would-be new total, so an overwrite of a large key with a small one can succeed even when near quota. The quota is per-plugin-per-task, not global.
Source
Thrown at src/core/plugin/capabilities/metadata.ts:136
.prepare<[string, string], { total: number }>(
`SELECT COALESCE(SUM(size), 0) AS total
FROM plugin_task_metadata
WHERE task_id = ? AND plugin_id = ?`
)
.get(taskId, pluginId) ?? { total: 0 }
const curRow = this.db
.prepare<[string, string, string], { size: number } | undefined>(
`SELECT size FROM plugin_task_metadata
WHERE task_id = ? AND plugin_id = ? AND key = ?`
)
.get(taskId, pluginId, key)
const currentSize = curRow?.size ?? 0
const projected = totRow.total - currentSize + projectedSize
if (projected > this.perPluginPerTaskBytes) {
throw new MetadataError(
'plugin.metadata.quota_exceeded',
`plugin.metadata.quota_exceeded: projected usage ${projected} exceeds quota ${this.perPluginPerTaskBytes}`
)
}
}
// -------------------------------------------------------------------------
// get
// -------------------------------------------------------------------------
async get(taskId: string, pluginId: string, key: string): Promise<unknown> {
const row = this.db
.prepare<[string, string, string], { value: string } | undefined>(
`SELECT value FROM plugin_task_metadata
WHERE task_id = ? AND plugin_id = ? AND key = ?`
)
.get(taskId, pluginId, key)
View on GitHub (pinned to 1a708ee577)
Solutions
- Delete or shrink keys you no longer need before writing new ones.
- Move large blobs (>64 KB) to the storage capability (default quota 5 MB) or fs.
- Raise perPluginPerTaskBytes in MetadataCapabilityHost options if the runtime policy allows.
- Compress or project values to the minimal JSON shape before set().
Example fix
// before
await metadata.set(taskId, pluginId, 'log', JSON.stringify(hugeLog))
// after
await metadata.delete(taskId, pluginId, 'log')
await metadata.set(taskId, pluginId, 'summary', { count, lastError }) Defensive patterns
Strategy: validation
Validate before calling
const projected = (currentTotal - currentKeySize) + Buffer.byteLength(json, 'utf8')
if (projected > perPluginPerTaskBytes) {
// delete unused keys first, or route the blob to storage/fs
} Try / catch
try {
await metadata.set(taskId, pluginId, key, value)
} catch (e) {
if (e instanceof MetadataError && e.code === 'plugin.metadata.quota_exceeded') {
await pruneOldKeys(taskId, pluginId)
await metadata.set(taskId, pluginId, key, value)
} else throw e
} Prevention
- Track per-(task,plugin) size as you write, so you can prune before hitting the wall.
- Route blobs >64 KB to storage (5 MB quota) or fs; reserve metadata for small state.
- Delete obsolete keys before writing new large ones to free projected space.
When it happens
Trigger: Accumulating many keys under one task/plugin; storing a single large JSON value (>64 KB) under one key; repeatedly writing larger and larger values without removing old ones.
Common situations: Plugin caches per-task results; plugin stores request/response logs; long-lived task accumulates state; default 64 KB quota smaller than plugin assumed.
Related errors
- plugin.storage.quota_exceeded
- plugin.fs.not_found
- plugin.fs.not_a_file
- plugin.fs.overwrite_required
- plugin.metadata.value_not_serializable
AI-assisted analysis of agalwood/Motrix@1a708ee577 (2026-08-12).
Data as JSON: /api/errors/8a757b0d9148af71.
Report an issue: GitHub.