{"record":{"id":"166839abd16a56d8","repo":"toeverything/AFFiNE","slug":"blob-invalid-166839","errorCode":"blob_invalid","errorMessage":"Blob size mismatch","messagePattern":"Blob size mismatch","errorType":"exception","errorClass":"BlobInvalid","httpStatus":400,"severity":"warning","filePath":"packages/backend/server/src/core/workspaces/resolvers/blob.ts","lineNumber":216,"sourceCode":"\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)\n      .workspace(workspaceId)\n      .assert('Workspace.Blobs.Write');\n\n    let record = await this.models.blob.get(workspaceId, key);\n    mime = mime || 'application/octet-stream';\n    if (record) {\n      if (record.size !== size) {\n        throw new BlobInvalid('Blob size mismatch');\n      }\n      if (record.mime !== mime) {\n        throw new BlobInvalid('Blob mime mismatch');\n      }\n\n      if (record.status === 'completed') {\n        const existingMetadata = await this.storage.head(workspaceId, key);\n        if (!existingMetadata) {\n          // record exists but object is missing, treat as a new upload\n          record = null;\n        } else if (existingMetadata.contentLength !== size) {\n          throw new BlobInvalid('Blob size mismatch');\n        } else if (existingMetadata.contentType !== mime) {\n          throw new BlobInvalid('Blob mime mismatch');\n        } else {\n          return {\n            method: BlobUploadMethod.GRAPHQL,\n            blobKey: key,","sourceCodeStart":198,"sourceCodeEnd":234,"githubUrl":"https://github.com/toeverything/AFFiNE/blob/26c515e050211269e911f7d9cfe162a26c83ed98/packages/backend/server/src/core/workspaces/resolvers/blob.ts#L198-L234","documentation":"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.","triggerScenarios":"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).","commonSituations":"Reusing a blob key for a different file; resume after the file was swapped; client computed size differently (pre/post normalization).","solutions":["Use a fresh blob key for a different file.","Send the exact same size as the original upload when resuming.","If the record is stale/wrong, remove it and start a new upload."],"exampleFix":"// before\ninitUpload(key, newSize, mime) // record.size !== newSize\n// after\nif (record && record.size !== newSize) initUpload(freshKey(), newSize, mime)","handlingStrategy":"validation","validationCode":"const rec = await models.blob.get(ws, key)\nif (rec && rec.size !== size) throw new ClientError('size mismatch; use a new key')","typeGuard":null,"tryCatchPattern":"try { await initUpload(key, size, mime) } catch (e) {\n  if (e.code === 'blob_invalid') initUpload(newKey(), size, mime)\n  else throw e\n}","preventionTips":["Use one key per distinct file.","Pass the canonical size when resuming.","Do not reuse keys across file versions."],"tags":["blobs","input-validation","upload","multipart"],"backgroundTag":null,"analyzedSha":"26c515e050211269e911f7d9cfe162a26c83ed98","analyzedAt":"2026-08-12T13:15:16.447Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}