toeverything/AFFiNE · error · BlobQuotaExceeded

blob_quota_exceeded

blob_quota_exceeded

Error message

You have exceeded your blob size quota.

What it means

Thrown by BlobResolver.setBlob when the workspace quota calculator evaluated at zero additional bytes (checkExceeded(0)) already reports blobQuotaExceeded — the workspace is over its per-plan blob quota before this upload is even counted. Distinct from StorageQuotaExceeded (total bytes) one line below; this one tracks the blob-count/size limit of the plan.

Solutions

  1. Free up blobs: delete unused blobs/docs so GC reclaims quota
  2. Upgrade the workspace plan to raise the blob quota
  3. Query the workspace quota before uploading and gate the UI with a quota indicator
  4. Reduce blob churn (dedupe, compress) if the workspace legitimately needs many blobs
Defensive patterns

Strategy: validation

Validate before calling

// check quota before uploading
const quota = await getWorkspaceQuota(wsId); // e.g. { blobQuota, blobUsage, storageQuota, storageUsage }
if (quota.blobUsage >= quota.blobQuota) {
  return showBlobQuotaExceededUI(quota);
}
await setBlob(wsId, file);

Type guard

function isBlobQuotaExceeded(e: unknown): e is { code: 'blob_quota_exceeded' } {
  return typeof e === 'object' && e !== null && (e as any).code === 'blob_quota_exceeded';
}

Try / catch

try {
  return await setBlob(wsId, file);
} catch (e) {
  if (isBlobQuotaExceeded(e)) {
    return promptUpgradeOrCleanup('blob'); // won't resolve by retrying the same upload
  }
  throw e;
}

Prevention

When it happens

Trigger: GraphQL setBlob (single-shot blob upload) on a workspace whose plan blob limit is already exceeded — the pre-check at size 0 fails immediately, before the stream is read.

Common situations: Free/low-tier workspaces after bulk image imports; plan downgrade leaving usage above the new limit; accumulated orphan blobs never garbage-collected.

Related errors


AI-assisted analysis of toeverything/AFFiNE@b4c8548c09 (2026-08-18). Data as JSON: /api/errors/095f0cd1e6b72539. Report an issue: GitHub.

Appendix: source

Thrown at packages/backend/server/src/core/workspaces/resolvers/blob.ts:188

  @Mutation(() => String)
  async setBlob(
    @CurrentUser() user: CurrentUser,
    @Args('workspaceId') workspaceId: string,
    @Args({ name: 'blob', type: () => GraphQLUpload })
    blob: FileUpload
  ) {
    await this.ac
      .user(user.id)
      .workspace(workspaceId)
      .assert('Workspace.Blobs.Write');

    const checkExceeded =
      await this.quota.getWorkspaceQuotaCalculator(workspaceId);

    let result = checkExceeded(0);
    if (result?.blobQuotaExceeded) {
      throw new BlobQuotaExceeded();
    } else if (result?.storageQuotaExceeded) {
      throw new StorageQuotaExceeded();
    }

    const buffer = await readBuffer(blob.createReadStream(), checkExceeded);

    await this.storage.put(workspaceId, blob.filename, buffer);
    return blob.filename;
  }

  @Mutation(() => BlobUploadInit)
  async createBlobUpload(
    @CurrentUser() user: CurrentUser,
    @Args('workspaceId') workspaceId: string,
    @Args('key') key: string,
    @Args('size', { type: () => Int }) size: number,
    @Args('mime') mime: string
  ): Promise<BlobUploadInit> {

View on GitHub (pinned to b4c8548c09)