toeverything/AFFiNE · error · BlobInvalid
blob_invalid
blob_invalid
Error message
Invalid endpoint
What it means
Thrown by R2UploadController.getUploadProxyConfig when the configured blob storage provider is neither 'cloudflare-r2' nor 'aws-s3'. The presigned-URL proxy upload path only supports S3-compatible backends, so any other provider (filesystem, minio-via-other-name, etc.) is rejected before signing a token.
Source
Thrown at packages/backend/server/src/core/storage/r2-proxy.ts:42
type UploadProxyConfig = {
signKey: string;
};
@Controller(STORAGE_PROXY_ROOT)
export class R2UploadController {
private readonly logger = new Logger(R2UploadController.name);
constructor(
private readonly config: Config,
private readonly models: Models,
private readonly rt: StorageRuntimeProvider
) {}
private getUploadProxyConfig(): UploadProxyConfig {
const storage = this.config.storages.blob.storage as StorageProviderConfig;
if (storage.provider !== 'cloudflare-r2' && storage.provider !== 'aws-s3') {
throw new BlobInvalid('Invalid endpoint');
}
const uploadConfig = (storage.config as S3StorageConfig).usePresignedURL;
const signKey = uploadConfig?.signKey;
if (!uploadConfig?.enabled || !signKey) {
throw new BlobInvalid('Invalid endpoint');
}
return { signKey };
}
private safeEqual(expected: string, actual: string) {
const a = Buffer.from(expected);
const b = Buffer.from(actual);
if (a.length !== b.length) {
return false;
}
return timingSafeEqual(a, b);View on GitHub (pinned to 26c515e050)
Solutions
- Set the blob storage provider to 'cloudflare-r2' or 'aws-s3' if you want to use the proxy upload path.
- If using a different backend, disable the presigned-URL proxy flow and use the standard blob upload resolver instead.
- Double-check the loaded Config.storages.blob value at startup.
Example fix
// before
AFFiNE_STORAGE_BLOB_PROVIDER=fs
// after
AFFiNE_STORAGE_BLOB_PROVIDER=cloudflare-r2
AFFiNE_STORAGE_R2_CONFIG='{"usePresignedURL":{"enabled":true,"signKey":"..."}}' Defensive patterns
Strategy: validation
Validate before calling
const provider = config.storages.blob.storage.provider;
if (provider !== 'cloudflare-r2' && provider !== 'aws-s3') {
// disable the proxy upload route in the client; use standard blob upload
setProxyUploadEnabled(false);
} Type guard
type S3LikeProvider = 'cloudflare-r2' | 'aws-s3';
function isS3Like(p: string): p is S3LikeProvider {
return p === 'cloudflare-r2' || p === 'aws-s3';
} Try / catch
try {
await proxyUpload(blob);
} catch (e) {
if (e?.code === 'blob_invalid' && /endpoint/.test(e.message)) {
// fall back to the GraphQL blob upload resolver
return graphqlUpload(blob);
}
throw e;
} Prevention
- Confirm the storage provider env var matches an S3-compatible backend before enabling the proxy.
- Run a startup self-check that calls getUploadProxyConfig and logs the active provider.
- Keep client routing in sync with the server's configured storage backend.
When it happens
Trigger: Client PUTs to the blob upload proxy while config.storages.blob.storage.provider is set to something other than cloudflare-r2/aws-s3 (e.g. 'fs', 'azure-blob'); misnamed provider string in the AFFiNE_STORAGE_R2 config.
Common situations: Self-host with filesystem storage but the client was routed to the proxy endpoint; migrating storage backends and forgetting to update client routing; typo in the provider env var.
Related errors
AI-assisted analysis of toeverything/AFFiNE@26c515e050 (2026-08-12).
Data as JSON: /api/errors/44b1773cb97ca1af.
Report an issue: GitHub.