{"record":{"id":"44b1773cb97ca1af","repo":"toeverything/AFFiNE","slug":"blob-invalid","errorCode":"blob_invalid","errorMessage":"Invalid endpoint","messagePattern":"Invalid endpoint","errorType":"exception","errorClass":"BlobInvalid","httpStatus":400,"severity":"error","filePath":"packages/backend/server/src/core/storage/r2-proxy.ts","lineNumber":42,"sourceCode":"\ntype UploadProxyConfig = {\n  signKey: string;\n};\n\n@Controller(STORAGE_PROXY_ROOT)\nexport class R2UploadController {\n  private readonly logger = new Logger(R2UploadController.name);\n\n  constructor(\n    private readonly config: Config,\n    private readonly models: Models,\n    private readonly rt: StorageRuntimeProvider\n  ) {}\n\n  private getUploadProxyConfig(): UploadProxyConfig {\n    const storage = this.config.storages.blob.storage as StorageProviderConfig;\n    if (storage.provider !== 'cloudflare-r2' && storage.provider !== 'aws-s3') {\n      throw new BlobInvalid('Invalid endpoint');\n    }\n    const uploadConfig = (storage.config as S3StorageConfig).usePresignedURL;\n    const signKey = uploadConfig?.signKey;\n    if (!uploadConfig?.enabled || !signKey) {\n      throw new BlobInvalid('Invalid endpoint');\n    }\n    return { signKey };\n  }\n\n  private safeEqual(expected: string, actual: string) {\n    const a = Buffer.from(expected);\n    const b = Buffer.from(actual);\n\n    if (a.length !== b.length) {\n      return false;\n    }\n\n    return timingSafeEqual(a, b);","sourceCodeStart":24,"sourceCodeEnd":60,"githubUrl":"https://github.com/toeverything/AFFiNE/blob/2af30773aecd567f09b346e7b72fc69143144057/packages/backend/server/src/core/storage/r2-proxy.ts#L24-L60","documentation":"BlobInvalid('Invalid endpoint') at packages/backend/server/src/core/storage/r2-proxy.ts:42. getUploadProxyConfig only proxies presigned uploads for S3-compatible providers 'cloudflare-r2' and 'aws-s3'; any other value of config.storages.blob.storage.provider aborts with this error.","triggerScenarios":"PUT /upload on the R2 upload proxy while the blob storage provider is configured as filesystem or anything other than cloudflare-r2/aws-s3.","commonSituations":"Local dev defaults (non-S3 storage) pointed at the proxy, a provider name typo in config, or routing all uploads through the proxy regardless of backend.","solutions":["Set the blob storage provider to cloudflare-r2 or aws-s3 in the server configuration.","Do not direct clients to the upload proxy endpoint when running non-S3 storage."],"exampleFix":"# before\n[blob]\nstorage.provider = 'fs'\n\n# after\n[blob.storage]\nprovider = 'cloudflare-r2'","handlingStrategy":"validation","validationCode":"const provider = config.storages.blob.storage.provider;\nconst proxySupported = provider === 'cloudflare-r2' || provider === 'aws-s3';\nif (!proxySupported) {\n  throw new Error(`upload proxy unavailable for provider ${provider}`);\n}","typeGuard":"const isBlobInvalid = (e: unknown): e is BlobInvalid => e instanceof BlobInvalid;","tryCatchPattern":"try {\n  await proxy.upload(...);\n} catch (e) {\n  if (e instanceof BlobInvalid && e.message === 'Invalid endpoint') {\n    // storage backend is not S3-compatible; route via direct server upload instead\n  }\n}","preventionTips":["Fail fast at startup: assert the blob provider is S3-compatible before mounting the proxy.","Keep environment-specific configs aligned so dev and prod use supported providers."],"tags":["storage","r2","s3","configuration","upload"],"backgroundTag":"invalid-configuration","analyzedSha":"2af30773aecd567f09b346e7b72fc69143144057","analyzedAt":"2026-08-18T21:16:52.546Z","contentChangedAt":"2026-08-18T21:16:52.546Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}