{"record":{"id":"76c76f40d48728fa","repo":"toeverything/AFFiNE","slug":"storage-quota-exceeded","errorCode":"storage_quota_exceeded","errorMessage":"You have exceeded your storage quota.","messagePattern":"You have exceeded your storage quota\\.","errorType":"exception","errorClass":"StorageQuotaExceeded","httpStatus":402,"severity":"error","filePath":"packages/backend/server/src/core/workspaces/resolvers/blob.ts","lineNumber":190,"sourceCode":"  async setBlob(\n    @CurrentUser() user: CurrentUser,\n    @Args('workspaceId') workspaceId: string,\n    @Args({ name: 'blob', type: () => GraphQLUpload })\n    blob: FileUpload\n  ) {\n    await this.ac\n      .user(user.id)\n      .workspace(workspaceId)\n      .assert('Workspace.Blobs.Write');\n\n    const checkExceeded =\n      await this.quota.getWorkspaceQuotaCalculator(workspaceId);\n\n    let result = checkExceeded(0);\n    if (result?.blobQuotaExceeded) {\n      throw new BlobQuotaExceeded();\n    } else if (result?.storageQuotaExceeded) {\n      throw new StorageQuotaExceeded();\n    }\n\n    const buffer = await readBuffer(blob.createReadStream(), checkExceeded);\n\n    await this.storage.put(workspaceId, blob.filename, buffer);\n    return blob.filename;\n  }\n\n  @Mutation(() => BlobUploadInit)\n  async createBlobUpload(\n    @CurrentUser() user: CurrentUser,\n    @Args('workspaceId') workspaceId: string,\n    @Args('key') key: string,\n    @Args('size', { type: () => Int }) size: number,\n    @Args('mime') mime: string\n  ): Promise<BlobUploadInit> {\n    await this.ac\n      .user(user.id)","sourceCodeStart":172,"sourceCodeEnd":208,"githubUrl":"https://github.com/toeverything/AFFiNE/blob/b4c8548c09da21b2898443559a5b846f0ccf5dd8/packages/backend/server/src/core/workspaces/resolvers/blob.ts#L172-L208","documentation":"Thrown by BlobResolver.setBlob when checkExceeded(0) reports storageQuotaExceeded — the workspace's total storage usage is already at or over the plan's storage quota, so the upload is refused before reading the stream. Sibling of BlobQuotaExceeded in the same pre-check; this one is about aggregate bytes.","triggerScenarios":"GraphQL setBlob on a workspace whose total storage (blobs + other counted usage) already exceeds the plan limit; the zero-byte probe failing means even a 1-byte upload would be rejected.","commonSituations":"Large media libraries on small plans; usage left over after a downgrade; storage accounting including pending uploads inflating the total.","solutions":["Free storage: delete large blobs / prune old data to drop usage below quota","Upgrade the plan's storage quota","Check current usage vs quota via the quota query before attempting uploads","Audit for orphaned objects inflating counted usage"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"const quota = await getWorkspaceQuota(wsId);\nif (quota.storageUsage >= quota.storageQuota) {\n  return showStorageQuotaExceededUI(quota);\n}\nawait setBlob(wsId, file);","typeGuard":"function isStorageQuotaExceeded(e: unknown): e is { code: 'storage_quota_exceeded' } {\n  return typeof e === 'object' && e !== null && (e as any).code === 'storage_quota_exceeded';\n}","tryCatchPattern":"try {\n  return await setBlob(wsId, file);\n} catch (e) {\n  if (isStorageQuotaExceeded(e)) {\n    return offerFreeStorageOrUpgrade(); // hard stop — do not retry blindly\n  }\n  throw e;\n}","preventionTips":["Show storage usage vs quota before large uploads","Provide cleanup tooling for old/large blobs","Check remaining quota against file size before starting an upload","Alert users after plan downgrades when usage exceeds the new limit"],"tags":["quota","storage","plan-limits","upload"],"backgroundTag":"storage-quota-exceeded","analyzedSha":"b4c8548c09da21b2898443559a5b846f0ccf5dd8","analyzedAt":"2026-08-18T21:16:52.546Z","contentChangedAt":"2026-08-18T21:16:52.546Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}