toeverything/AFFiNE · warning · BlobInvalid

blob_invalid

blob_invalid

Error message

Blob size mismatch

What it means

Thrown on blob upload init when an existing blob record is found and record.size differs from the size argument the client sent. The client is trying to (re-)init an upload for a key whose recorded size does not match.

Source

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

  @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> {
    await this.ac
      .user(user.id)
      .workspace(workspaceId)
      .assert('Workspace.Blobs.Write');

    let record = await this.models.blob.get(workspaceId, key);
    mime = mime || 'application/octet-stream';
    if (record) {
      if (record.size !== size) {
        throw new BlobInvalid('Blob size mismatch');
      }
      if (record.mime !== mime) {
        throw new BlobInvalid('Blob mime mismatch');
      }

      if (record.status === 'completed') {
        const existingMetadata = await this.storage.head(workspaceId, key);
        if (!existingMetadata) {
          // record exists but object is missing, treat as a new upload
          record = null;
        } else if (existingMetadata.contentLength !== size) {
          throw new BlobInvalid('Blob size mismatch');
        } else if (existingMetadata.contentType !== mime) {
          throw new BlobInvalid('Blob mime mismatch');
        } else {
          return {
            method: BlobUploadMethod.GRAPHQL,
            blobKey: key,

View on GitHub (pinned to 26c515e050)

Solutions

  1. Use a fresh blob key for a different file.
  2. Send the exact same size as the original upload when resuming.
  3. If the record is stale/wrong, remove it and start a new upload.

Example fix

// before
initUpload(key, newSize, mime) // record.size !== newSize
// after
if (record && record.size !== newSize) initUpload(freshKey(), newSize, mime)
Defensive patterns

Strategy: validation

Validate before calling

const rec = await models.blob.get(ws, key)
if (rec && rec.size !== size) throw new ClientError('size mismatch; use a new key')

Try / catch

try { await initUpload(key, size, mime) } catch (e) {
  if (e.code === 'blob_invalid') initUpload(newKey(), size, mime)
  else throw e
}

Prevention

When it happens

Trigger: Client calls createBlobUpload with a key that already has a record but passes a different size than stored (file changed, or a stale resume token).

Common situations: Reusing a blob key for a different file; resume after the file was swapped; client computed size differently (pre/post normalization).

Related errors


AI-assisted analysis of toeverything/AFFiNE@26c515e050 (2026-08-12). Data as JSON: /api/errors/166839abd16a56d8. Report an issue: GitHub.