{"record":{"id":"8a757b0d9148af71","repo":"agalwood/Motrix","slug":"plugin-metadata-quota-exceeded","errorCode":"plugin.metadata.quota_exceeded","errorMessage":"plugin.metadata.quota_exceeded: projected usage ${projected} exceeds quota ${this.perPluginPerTaskBytes}","messagePattern":"plugin\\.metadata\\.quota_exceeded: projected usage (.+?) exceeds quota (.+?)","errorType":"exception","errorClass":"MetadataError","httpStatus":null,"severity":"error","filePath":"src/core/plugin/capabilities/metadata.ts","lineNumber":136,"sourceCode":"      .prepare<[string, string], { total: number }>(\n        `SELECT COALESCE(SUM(size), 0) AS total\n         FROM plugin_task_metadata\n         WHERE task_id = ? AND plugin_id = ?`\n      )\n      .get(taskId, pluginId) ?? { total: 0 }\n\n    const curRow = this.db\n      .prepare<[string, string, string], { size: number } | undefined>(\n        `SELECT size FROM plugin_task_metadata\n         WHERE task_id = ? AND plugin_id = ? AND key = ?`\n      )\n      .get(taskId, pluginId, key)\n\n    const currentSize = curRow?.size ?? 0\n    const projected = totRow.total - currentSize + projectedSize\n\n    if (projected > this.perPluginPerTaskBytes) {\n      throw new MetadataError(\n        'plugin.metadata.quota_exceeded',\n        `plugin.metadata.quota_exceeded: projected usage ${projected} exceeds quota ${this.perPluginPerTaskBytes}`\n      )\n    }\n  }\n\n  // -------------------------------------------------------------------------\n  // get\n  // -------------------------------------------------------------------------\n\n  async get(taskId: string, pluginId: string, key: string): Promise<unknown> {\n    const row = this.db\n      .prepare<[string, string, string], { value: string } | undefined>(\n        `SELECT value FROM plugin_task_metadata\n         WHERE task_id = ? AND plugin_id = ? AND key = ?`\n      )\n      .get(taskId, pluginId, key)\n","sourceCodeStart":118,"sourceCodeEnd":154,"githubUrl":"https://github.com/agalwood/Motrix/blob/1a708ee57746c434e2c67a44bbf0906a976afea4/src/core/plugin/capabilities/metadata.ts#L118-L154","documentation":"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.","triggerScenarios":"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.","commonSituations":"Plugin caches per-task results; plugin stores request/response logs; long-lived task accumulates state; default 64 KB quota smaller than plugin assumed.","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()."],"exampleFix":"// before\nawait metadata.set(taskId, pluginId, 'log', JSON.stringify(hugeLog))\n\n// after\nawait metadata.delete(taskId, pluginId, 'log')\nawait metadata.set(taskId, pluginId, 'summary', { count, lastError })","handlingStrategy":"validation","validationCode":"const projected = (currentTotal - currentKeySize) + Buffer.byteLength(json, 'utf8')\nif (projected > perPluginPerTaskBytes) {\n  // delete unused keys first, or route the blob to storage/fs\n}","typeGuard":null,"tryCatchPattern":"try {\n  await metadata.set(taskId, pluginId, key, value)\n} catch (e) {\n  if (e instanceof MetadataError && e.code === 'plugin.metadata.quota_exceeded') {\n    await pruneOldKeys(taskId, pluginId)\n    await metadata.set(taskId, pluginId, key, value)\n  } else throw e\n}","preventionTips":["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."],"tags":["metadata","quota","storage","limit"],"backgroundTag":null,"analyzedSha":"1a708ee57746c434e2c67a44bbf0906a976afea4","analyzedAt":"2026-08-12T16:18:09.346Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}