toeverything/AFFiNE · error · BlobNotFound

blob_not_found

blob_not_found

Error message

Blob ${blobId} not found in Space ${spaceId}.

What it means

Thrown by the workspace blob route when storage.get(workspaceId, name, true) returns neither a body nor a redirectUrl — the named blob object does not exist in the workspace's blob storage. Permission already passed; this is a pure storage-layer miss.

Solutions

  1. Verify the blob key exists (blobs list API or the storage backend) before linking it
  2. Re-upload the missing blob and update the referencing doc
  3. If objects vanish systematically, audit storage provider config (bucket, prefix) and any GC job
  4. Watch for the route's 'has no metadata' warning in logs — it flags half-written objects
Defensive patterns

Strategy: try-catch

Type guard

function isBlobNotFound(e: unknown): e is { code: 'blob_not_found'; spaceId: string; blobId: string } {
  return typeof e === 'object' && e !== null && (e as any).code === 'blob_not_found';
}

Try / catch

try {
  return await fetchBlob(wsId, blobKey);
} catch (e) {
  if (isBlobNotFound(e)) {
    return placeholderImage(e.blobId); // media gone — degrade gracefully, no retry
  }
  throw e;
}

Prevention

When it happens

Trigger: GET /api/workspaces/:id/blobs/:name with a name never uploaded, deleted, or whose object is missing from the backend even though the URL was well-formed (no signed-url redirect available either).

Common situations: Stale blob references in doc content after blob garbage collection; storage bucket wiped or migrated without copying objects; truncated/typo'd blob key; upload recorded in the metadata store but the object write failed.

Understand the failure class

Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.

Related errors


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

Appendix: source

Thrown at packages/backend/server/src/core/workspaces/controller.ts:137

    const { body, metadata, redirectUrl } = await this.storage.get(
      workspaceId,
      name,
      true
    );

    if (redirectUrl) {
      // redirect to signed url
      if (redirect === 'manual') {
        return res.send({
          url: redirectUrl,
        });
      } else {
        return res.redirect(redirectUrl);
      }
    }

    if (!body) {
      throw new BlobNotFound({
        spaceId: workspaceId,
        blobId: name,
      });
    }

    // metadata should always exists if body is not null
    if (metadata) {
      res.setHeader(
        'content-type',
        metadata.contentType.startsWith('application/json') // application/json is reserved for redirect url
          ? 'text/json'
          : metadata.contentType
      );
      res.setHeader('last-modified', metadata.lastModified.toUTCString());
      res.setHeader('content-length', metadata.contentLength);
    } else {
      this.logger.warn(`Blob ${workspaceId}/${name} has no metadata`);
    }

View on GitHub (pinned to b4c8548c09)