@vercel/blob
semver
>=0.19.0postconditions32functions18last verified2026-06-24coverage score90%Postconditions: what we check
- put · blob-put-no-try-catcherrorWhenMissing BLOB_READ_WRITE_TOKEN (BlobAccessError), content-type not allowed (BlobContentTypeNotAllowedError), storage quota exceeded (BlobStoreLimitReachedError), network failure, or Vercel API unavailable (BlobUnknownError).Throws
BlobAccessError, BlobContentTypeNotAllowedError, BlobStoreLimitReachedError, or BlobUnknownError. All extend BlobError which extends Error.Required handlingCaller MUST wrap put() in try-catch. Missing BLOB_READ_WRITE_TOKEN causes immediate BlobAccessError (common in deployments that forgot to set the env var). Storage quota errors are hard to predict and will crash file upload endpoints if unhandled. Minimum handling: try { const blob = await put('file.pdf', content, { access: 'public' }); return blob.url; } catch (err) { if (err instanceof BlobAccessError) { throw new Error('Blob storage not configured'); } throw err; }costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible - del · blob-del-no-try-catcherrorWhenMissing auth token, network failure, or Vercel API unavailable.Throws
BlobAccessError or BlobUnknownError.Required handlingCaller MUST wrap del() in try-catch. Failed deletions leave orphaned blobs that incur storage costs.costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisibleSources[2] - list · blob-list-no-try-catcherrorWhenMissing auth token or network failure.Throws
BlobAccessError or BlobUnknownError.Required handlingCaller MUST wrap list() in try-catch.costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisibleSources[2] - head · blob-head-no-try-catcherrorWhenhead() THROWS BlobNotFoundError when the blob does not exist — it does NOT return null.Throws
BlobNotFoundError, BlobAccessError, BlobStoreNotFoundError, or BlobUnknownError.Required handlingCaller MUST handle this condition appropriately.costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisibleSources[1] - head · blob-head-access-errorerrorWhenhead() throws BlobAccessError when BLOB_READ_WRITE_TOKEN is missing, invalid, or the token does not have access to the requested store.Throws
BlobAccessError or BlobStoreNotFoundError.Required handlingCaller MUST handle this condition appropriately.costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisibleSources[1] - copy · blob-copy-no-try-catcherrorWhencopy() throws on any error: BlobAccessError (auth failure), BlobNotFoundError (source blob doesn't exist), BlobUnknownError (network failure), or BlobPreconditionFailedError (ETag mismatch when ifMatch is used).Throws
BlobAccessError, BlobNotFoundError, BlobUnknownError, or BlobPreconditionFailedError.Required handlingCaller MUST handle this condition appropriately.costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisibleSources[1] - copy · blob-copy-source-not-founderrorWhencopy() throws BlobNotFoundError when the source blob (fromUrlOrPathname) does not exist.Throws
BlobNotFoundError.Required handlingCaller MUST handle this condition appropriately.costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisibleSources[1] - get · blob-get-null-not-checkederrorWhenget() returns null when the blob is not found (HTTP 404) — it does NOT throw.ReturnsGetBlobResult | null (null when blob not found — callers must null-check before accessing stream or blob).Required handlingCaller MUST handle this condition appropriately.costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisibleSources[1]
- get · blob-get-no-try-catcherrorWhenget() throws BlobAccessError (auth failure), BlobServiceRateLimited (HTTP 429, retryAfter seconds available on error), or BlobServiceNotAvailable (Vercel API down).Throws
BlobAccessError, BlobServiceRateLimited, or BlobServiceNotAvailable.Required handlingCaller MUST handle this condition appropriately.costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisibleSources[1] - handleUpload · blob-handle-upload-missing-auth-checkerrorWhenonBeforeGenerateToken must authenticate the user before returning token options.Throws
BlobAccessError or any error thrown from onBeforeGenerateToken/onUploadCompleted callbacks.Required handlingCaller MUST handle this condition appropriately.costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisibleSources[1] - handleUpload · blob-handle-upload-no-try-catcherrorWhenhandleUpload() itself throws BlobAccessError when BLOB_READ_WRITE_TOKEN is missing or invalid.Throws
BlobAccessError when BLOB_READ_WRITE_TOKEN is missing or invalid, or re-throws from callbacks.Required handlingCaller MUST handle this condition appropriately.costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisibleSources[1] - createMultipartUpload · blob-create-multipart-no-try-catcherrorWhencreateMultipartUpload() throws BlobAccessError (auth failure), BlobServiceRateLimited (rate limit with retryAfter), or BlobUnknownError (network failure).Throws
BlobAccessError, BlobServiceRateLimited, or BlobUnknownError.Required handlingCaller MUST handle this condition appropriately.costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisibleSources[1] - uploadPart · blob-upload-part-no-try-catcherrorWhenuploadPart() throws BlobAccessError, BlobFileTooLargeError, BlobUnknownError, or BlobServiceRateLimited.Throws
BlobAccessError, BlobFileTooLargeError, BlobUnknownError, or BlobServiceRateLimited.Required handlingCaller MUST handle this condition appropriately.costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisibleSources[1] - uploadPart · blob-upload-part-minimum-sizewarningWhenEach part (except the last) must be at least 5MB.Throws
BlobFileTooLargeError when a part is smaller than 5MB (except the last part).Required handlingCaller MUST handle this condition appropriately.costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisibleSources[1] - completeMultipartUpload · blob-complete-multipart-no-try-catcherrorWhencompleteMultipartUpload() throws BlobAccessError (auth failure), BlobUnknownError (network failure or invalid parts), or BlobServiceNotAvailable.Throws
BlobAccessError, BlobUnknownError, or BlobServiceNotAvailable.Required handlingCaller MUST handle this condition appropriately.costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisibleSources[1] - upload · blob-upload-missing-handle-urlerrorWhenhandleUploadUrl option is not provided to upload(). This is a required parameter — upload() cannot work without a server route to fetch a client token from.Throws
BlobError: client/`upload` requires the 'handleUploadUrl' parameterRequired handlingCaller MUST always provide handleUploadUrl. This error throws synchronously during options validation before any network request is made — it is a programming error, not a runtime failure. Add a validation check at component initialization time.costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible - upload · blob-upload-token-fetch-failederrorWhenThe handleUploadUrl server route returns a non-2xx response (auth failure, server error, route not found), or the response body is not valid JSON containing clientToken. Common causes: missing authentication middleware on the handleUpload route, route misconfiguration, or server returning HTML error page instead of JSON.Throws
BlobError: Failed to retrieve the client tokenRequired handlingCaller MUST wrap upload() in try-catch. The token fetch is the first network request in the upload flow — if it fails, the upload never starts. Common mistake: the handleUpload route is protected by auth middleware that redirects unauthenticated requests to /login (HTML, not JSON), causing this error. Ensure the route returns JSON even on auth failure.costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible - upload · blob-upload-server-only-options-usederrorWhenCaller passes addRandomSuffix, cacheControlMaxAge, allowOverwrite, or ifMatch to upload(). These options are only valid for server-side put(). They must be configured in onBeforeGenerateToken on the server side, not in the client-side upload() call.Throws
BlobError: client/`upload` doesn't allow `addRandomSuffix`, `cacheControlMaxAge`, `allowOverwrite` or `ifMatch`. Configure these options at the server side when generating client tokens.Required handlingMove storage configuration options to the server-side onBeforeGenerateToken callback in your handleUpload route. Do not pass them to upload() on the client. The client token embeds these settings from the server at token-generation time.costlowin prodimmediate exceptionusers seeservice unavailablevisibilityvisible - upload · blob-upload-client-token-expiredwarningWhenThe client token has expired (default TTL: 1 hour from generateClientTokenFromReadWriteToken call). This happens when: the user keeps the file picker open for more than an hour before clicking upload, or when tokens are pre-generated and cached longer than their TTL.Throws
BlobClientTokenExpiredError: Client token has expired.Required handlingCaller MUST catch BlobClientTokenExpiredError separately from other errors and prompt the user to retry (which will generate a fresh token). Do not treat expired token as a permanent failure. The BlobClientTokenExpiredError class is exported from '@vercel/blob/client' for instanceof checks.costlowin prodimmediate exceptionusers seeservice unavailablevisibilityvisible - generateClientTokenFromReadWriteToken · blob-generate-client-token-browser-enverrorWhengenerateClientTokenFromReadWriteToken() is called from a browser environment (where typeof window !== 'undefined'). This function requires server-side crypto to sign the token payload and must never be called client-side as it would expose the BLOB_READ_WRITE_TOKEN to users.Throws
BlobError: "generateClientTokenFromReadWriteToken" must be called from a server environmentRequired handlingNever call generateClientTokenFromReadWriteToken() in client-side code (React components, browser scripts, client-side Next.js pages). It must only be called in server-side code: API routes, Server Actions, getServerSideProps, or route handlers in Next.js App Router. The read-write token must never be exposed to the browser.costhighin prodimmediate exceptionusers seeauthentication failurevisibilityvisible - generateClientTokenFromReadWriteToken · blob-generate-client-token-invalid-tokenerrorWhenThe BLOB_READ_WRITE_TOKEN is missing, malformed (doesn't contain the expected storeId segment after the 4th underscore), or the token option is passed but invalid. The token format is: vercel_blob_rw_<storeId>_<secret>.Throws
BlobError: Invalid `token` parameter (if token option given) or Invalid `BLOB_READ_WRITE_TOKEN`Required handlingCaller MUST wrap generateClientTokenFromReadWriteToken() in try-catch. This commonly fails in development when BLOB_READ_WRITE_TOKEN is not pulled via `vercel env pull`. Validate the environment variable is set at application startup. When this throws inside handleUpload(), the entire upload route fails with 500.costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible - generateClientTokenFromReadWriteToken · blob-generate-client-token-conflicting-optionswarningWhenBoth ifMatch and allowOverwrite: false are specified in the token options. These options are contradictory: ifMatch implies a conditional overwrite (allowOverwrite must be true), but allowOverwrite: false forbids overwrites entirely.Throws
BlobError: ifMatch and allowOverwrite: false are contradictory. ifMatch is used for conditional overwrites, which requires allowOverwrite to be true.Required handlingWhen using ifMatch for optimistic concurrency control, either omit allowOverwrite (the SDK will auto-set it to true) or explicitly pass allowOverwrite: true. Do not combine ifMatch with allowOverwrite: false.costlowin prodimmediate exceptionusers seeservice unavailablevisibilityvisible - createMultipartUploader · blob-create-multipart-uploader-no-try-catcherrorWhencreateMultipartUploader() throws BlobAccessError (auth failure), BlobServiceRateLimited (rate limit), BlobServiceNotAvailable (network failure / fetch failed), or BlobUnknownError. The initial call creates the upload session — if it fails, no session is created and no cleanup is needed.Throws
BlobAccessError, BlobServiceRateLimited, BlobServiceNotAvailable, or BlobUnknownError.Required handlingCaller MUST wrap the createMultipartUploader() call in try-catch. The returned uploader object's uploadPart() and complete() methods also throw and must each be individually caught. A failed uploadPart() does not automatically abort the session — the session remains open until complete() is called or it expires server-side.costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible - createMultipartUploader · blob-create-multipart-uploader-plain-object-bodyerrorWhenuploader.uploadPart() is called with a plain JavaScript object as the body (e.g., { content: 'data' }). The body must be a string, Buffer, ReadableStream, File, or Blob. This is a runtime validation error that throws synchronously.Throws
BlobError: Body must be a string, buffer or stream. You sent a plain JavaScript object, double check what you're trying to upload.Required handlingEnsure the body passed to uploader.uploadPart() is a proper binary or text type. Common mistake: passing a JS object that represents file metadata instead of the actual file content. Convert to Buffer, Blob, or ReadableStream before uploading.costlowin prodimmediate exceptionusers seeservice unavailablevisibilityvisible - createFolder · blob-create-folder-no-try-catcherrorWhencreateFolder() throws BlobAccessError (auth failure / missing token), BlobStoreSuspendedError (store has been suspended), BlobStoreNotFoundError (store in token references deleted store), BlobServiceRateLimited, BlobServiceNotAvailable (network failure), or BlobUnknownError.Throws
BlobAccessError, BlobStoreSuspendedError, BlobStoreNotFoundError, BlobServiceRateLimited, BlobServiceNotAvailable, or BlobUnknownError.Required handlingCaller MUST wrap createFolder() in try-catch. Although folder creation is typically a one-time setup operation, failures in file browser features must be handled to surface errors to users rather than silently creating an incomplete folder hierarchy. The returned url should be stored if the folder needs to be deleted later.costlowin prodimmediate exceptionusers seeservice unavailablevisibilityvisible - issueSignedToken · blob-issue-signed-token-no-try-catcherrorWhenissueSignedToken() throws BlobError on synchronous validation failures (missing options object, empty operations array, validUntil that is not a finite integer milliseconds timestamp, or validUntil <= now). It also throws BlobAccessError (auth failure), BlobStoreSuspendedError, BlobStoreNotFoundError, BlobServiceRateLimited, BlobServiceNotAvailable, or BlobUnknownError from the underlying requestApi() call to POST /signed-token.Throws
BlobError, BlobAccessError, BlobStoreSuspendedError, BlobStoreNotFoundError, BlobServiceRateLimited, BlobServiceNotAvailable, or BlobUnknownError.Required handlingCaller MUST wrap issueSignedToken() in try-catch. This is the first call in the presigned-URL workflow — if it fails, no presigned URL can be issued and the entire client upload / signed-URL session is aborted. Sync validation errors (BlobError on bad validUntil) are programming errors that should be caught at request-time and surfaced as 400 to the client, not as 500. Auth errors should be surfaced as 401/500 depending on whether the request originated server-side or proxied a client request.costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible - issueSignedToken · blob-issue-signed-token-valid-until-in-pasterrorWhenissueSignedToken() is called with validUntil that is <= Date.now() (a timestamp already in the past). This is a synchronous BlobError thrown before any network call. Common cause: passing a number-of-seconds (e.g. 3600) instead of an absolute ms timestamp (Date.now() + 3600 * 1000), or reusing a cached validUntil after the cache TTL elapsed.Throws
BlobError: `issueSignedToken`: validUntil must be in the future.Required handlingAlways compute validUntil as Date.now() + ttlMs, not a duration. Validate the value is a future timestamp before calling. If signing tokens are pre-computed, recompute validUntil at issuance time, not at scheduling time.costlowin prodimmediate exceptionusers seeservice unavailablevisibilityvisibleSources[9] - presignUrl · blob-presign-url-no-try-catcherrorWhenpresignUrl() throws BlobError synchronously for any of: missing clientSigningToken / delegationToken from the IssuedSignedToken pair, invalid or unreadable delegationToken payload, pathname mismatch (options.pathname does not match the scope encoded in the delegation), expired delegation (Date.now() > scope.validUntil), or operation not present in scope.operations (e.g. operation: 'put' against a get-only delegation).Throws
BlobError (sync — clientSigningToken/delegationToken required, invalid delegation payload, pathname mismatch, expired delegation, operation not authorized).Required handlingCaller MUST wrap presignUrl() in try-catch. These are all programming errors except the expired-delegation case, which is the normal expiry path and must be handled by issuing a fresh token via issueSignedToken() and retrying. Returning a 5xx for expired-delegation is wrong UX — surface a 400 with "presigned URL expired, retry" so the client can re-request a token.costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible - presignUrl · blob-presign-url-delegation-expiredwarningWhenpresignUrl() is called with a signedToken whose scope.validUntil has already passed (Date.now() > scope.validUntil). This is the dominant expected-failure mode in production: signed tokens are short-lived (default 1 hour) and any retry or queued job picked up after the TTL window will throw. Treating this as a generic error buries the legitimate retry path.Throws
BlobError: The signed delegation has expired; issue a new token first.Required handlingCatch this error explicitly and re-issue a fresh token via issueSignedToken(), then retry presignUrl(). Do NOT bubble it as a 5xx to the client — it is an expected condition. For background jobs, refresh the token at the start of each batch, not at job enqueue time.costlowin prodimmediate exceptionusers seeservice unavailablevisibilityvisibleSources[9] - handleUploadPresigned · blob-handle-upload-presigned-no-try-catcherrorWhenhandleUploadPresigned() throws BlobError on missing/invalid request body, signature verification failure (BLOB_WEBHOOK_PUBLIC_KEY mismatch on the upload-completed callback), or BlobAccessError on missing/invalid token. It also re-throws any error thrown by the getSignedToken or onUploadCompleted callbacks. Webhook signature mismatch is the dominant failure mode — happens whenever the public key env var drifts from the keypair Vercel signs callbacks with.Throws
BlobError (sync validation, signature verification failure), BlobAccessError (auth failure), or re-thrown errors from getSignedToken / onUploadCompleted callbacks.Required handlingThe outer route handler MUST wrap handleUploadPresigned() in try-catch and return an HTTP error response, not let the exception propagate. Signature-verification failures should be returned as 401 (callback rejected) — a 500 here would silently fail upload completion notifications. Validation errors from getSignedToken (e.g. pathname not allowed for this user) should be returned as 403.costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible - handleUploadPresigned · blob-handle-upload-presigned-missing-webhook-public-keyerrorWhenhandleUploadPresigned() is called without webhookPublicKey AND BLOB_WEBHOOK_PUBLIC_KEY env var is not set. Upload-completed callbacks cannot be verified — either the handler will reject all callbacks (no key configured), or in worst case it will accept unsigned callbacks and let an attacker spoof "upload completed" events to trigger your onUploadCompleted business logic (e.g. database inserts) without an actual upload occurring.Throws
BlobError on signature verification failure, or silently accepts spoofed callbacks if misconfigured.Required handlingSet BLOB_WEBHOOK_PUBLIC_KEY in your Vercel project env vars BEFORE deploying any route that uses handleUploadPresigned(). Treat a missing public key as a fatal startup error, not a warning. The keypair is generated by Vercel and must be fetched once via the dashboard.costhighin prodsilent failureusers seesecurity breachvisibilitysilentSources[3] - uploadPresigned · blob-upload-presigned-no-try-catcherrorWhenuploadPresigned() throws BlobError if handleUploadUrl is missing (programming error, synchronous), if the presigned-URL fetch from the server route fails (non-2xx or non-JSON response), if the presigned URL has expired between fetch and use (BlobClientTokenExpiredError-equivalent), or if the storage API rejects the upload (BlobAccessError, BlobFileTooLargeError, BlobServiceRateLimited, BlobUnknownError).Throws
BlobError, BlobAccessError, BlobFileTooLargeError, BlobServiceRateLimited, BlobUnknownError, or BlobClientTokenExpiredError-equivalent.Required handlingCaller MUST wrap uploadPresigned() in try-catch. Distinguish expired-presigned-URL (retry by re-fetching) from auth-rejected (surface auth error to user) from size-too-large (surface file-size error). A single catch-all "upload failed" message buries the legitimate retry path for expired URLs.costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
Sources
Every postcondition cites at least one of these. Grouped by source type; numbered to match the footnotes above.
Official documentation
- [1]vercel.com/docs/storage/vercel-blobUsing Blob Sdk
- [2]vercel.com/docs/storage/vercel-blobApi Reference
- [3]vercel.com/docs/vercel-blob/using-blob-sdkUsing Blob Sdk
- [6]vercel.com/docs/vercel-blob/using-blob-sdkUsing Blob Sdk
Source code
- [4]github.com/vercel/storage/blobvercel/storage · client.ts
- [5]github.com/vercel/storage/blobvercel/storage · api.ts
- [7]github.com/vercel/storage/blobvercel/storage · create-uploader.ts
- [8]github.com/vercel/storage/blobvercel/storage · create-folder.ts
- [9]github.com/vercel/storage/blobvercel/storage · signed-token.ts
Research notes
Curator notes from SOURCES.md captured when the profile was written so you can verify the reasoning, not just the rules.
@vercel/blob — Sources
Official Documentation
Error Types
| Error Class | Trigger |
|---|---|
BlobAccessError | Missing or invalid BLOB_READ_WRITE_TOKEN |
BlobUnknownError | Network failures, Vercel API unavailable |
BlobContentTypeNotAllowedError | Content-type not permitted by store policy |
BlobStoreLimitReachedError | Storage quota exceeded |
All error classes extend BlobError which extends Error.
Evidence Quality: partial
Documentation confirms errors are thrown but does not enumerate all edge cases.
All three functions (put, del, list) require BLOB_READ_WRITE_TOKEN and will
throw BlobAccessError if the env var is missing — very common in dev environments
and CI pipelines that haven't set the token.
Need a different package?
Request a profile