> ## Documentation Index
> Fetch the complete documentation index at: https://docs.uselayers.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Response types and error handling

> Reference for shared response shapes returned by Layers SDK controllers, the complete product model, and the tagged error model used for error handling.

## Controller state

All controllers expose reactive state via a `ReadonlySignal<QueryState<T>>`:

```typescript theme={null}
interface QueryState<T> {
  data: T | null
  error: ClientError | null
  isFetching: boolean
}
```

## Query result

Collection, search, blocks, and image search controllers all return the same base `QueryResult` shape in `data`:

```typescript theme={null}
interface QueryResult {
  products: Product[]
  totalResults: number
  totalPages: number
  page: number
  facets: Record<string, Record<string, number>>
  priceRange?: PriceRange
  attributionToken?: string
}
```

`priceRange` requires `{ name: 'Price', code: 'variants.price' }` in your `facets` configuration. Without it, `priceRange` will be `undefined`.

### Blocks result

Blocks results extend `QueryResult` with block metadata:

```typescript theme={null}
interface BlocksResult extends QueryResult {
  block?: BlocksInfo
}

interface BlocksInfo {
  id: string
  label: string
  title: string
  anchor_type: 'product' | 'collection' | 'cart' | 'none'
  strategy_type: 'interaction' | 'collection_interaction' | 'similar_products' | 'manual'
  strategy_key: string
}
```

### Suggestions result

The `suggest` controller returns a different shape:

```typescript theme={null}
interface Suggestions {
  matchedQueries: string[]
  originalQuery: string
  normalizedQuery: string
}
```

### Content search result

The `searchContent` controller returns articles:

```typescript theme={null}
interface SearchContentResult {
  articles: Article[]
  query: string
  contentType: string | null
  totalResults: number
  page: number
  resultsPerPage: number
}

interface Article {
  title: string
  handle: string
  summary: string
  author: string
  tags: string[]
  image: ArticleImage | null
  publishedAt: string
}

interface ArticleImage {
  url: string
  width: number
  height: number
}
```

## Product types

### Product

The `Product` type represents a fully built product returned by the SDK. It extends `ProductBase` and can be further extended via the `transforms.product` configuration.

```typescript theme={null}
type Product = ProductBase & Record<string, unknown>

interface ProductBase {
  // Identifiers
  id: CommerceBlocksID
  gid: ProductGID
  numericId: number

  // Core
  title: string
  handle: string
  vendor: string
  type: string
  availableForSale: boolean
  tags: string[]

  // Media
  images: Image[]
  featuredMedia: FeaturedMedia | null

  // Pricing
  price: Price
  compareAtPrice: Price | null

  // Options and variants
  options: RichProductOption[]
  selectedOptions: ProductOption[]
  variants: ProductVariant[]
  selectedVariant: ProductVariant | null

  // Metadata
  metafields: Record<string, Record<string, unknown>>
  calculated: Record<string, unknown>

  // Taxonomy
  category: ProductCategory | null

  // Combined listings
  combinedListingParentProductId: number | null
  combinedListingRole: string | null

  // Breakout
  breakoutOptions: ProductOption[]
  breakoutVariantId: number | null
}
```

`CommerceBlocksID` is a string in the format `layers-{numericId}`. `ProductGID` is a Shopify Global ID string like `gid://shopify/Product/123`.

### ProductVariant

```typescript theme={null}
interface ProductVariant {
  id: CommerceBlocksID
  gid: VariantGID
  numericId: number
  title: string
  sku: string | null
  position: number
  availableForSale: boolean
  price: Price
  compareAtPrice: Price | null
  image: Image | null
  selectedOptions: ProductOption[]
  inventoryLevels: Record<string, number>
  inventoryPolicy: 'DENY' | 'CONTINUE' | null
  inStockLocationIds: number[]
  requiresSellingPlan: boolean
  hasSellingPlan: boolean
}
```

`VariantGID` is a Shopify Global ID string like `gid://shopify/ProductVariant/456`.

### Price and PriceRange

```typescript theme={null}
interface Price {
  amount: number
  currencyCode: string
  formatted: string
}

interface PriceRange {
  min: Price
  max: Price
}
```

The `formatted` value is generated by the SDK using the `formatPrice` function you provide at client init, or a default formatter based on `Intl.NumberFormat`.

### Image and FeaturedMedia

```typescript theme={null}
interface Image {
  url: string
  alt: string
  width: number
  height: number
  position: number
  variantIds: number[]
}

interface FeaturedMedia {
  mediaContentType: string | null
  url: string
  alt: string
  width: number
  height: number
}
```

On shallow products (the default), `featuredMedia` is sourced from the matched variant's featured media. It falls back to the product-level featured media when the variant has none. Account for this fallback in product cards so missing variant media does not appear as a missing image.

### Options

```typescript theme={null}
interface ProductOption {
  name: string
  value: string
}

interface RichProductOption extends ProductOption {
  code: string
  swatch?: Swatch
}
```

`RichProductOption` is used for the product's top-level `options` array. It includes the attribute `code` (the internal Layers identifier) and an optional `swatch` with color or image data if defined for that option.

### ProductCategory

```typescript theme={null}
interface ProductCategory {
  id: string
  name: string
  fullName: string
  isLeaf: boolean
  isRoot: boolean
}
```

### NamedTag

```typescript theme={null}
interface NamedTag {
  name: string
  value: string | number | boolean | string[]
}
```

## Error handling

All methods return `{ data, error }` instead of throwing. Errors are discriminated by `_tag`:

```typescript theme={null}
const result = await collection.execute()

if (result.error) {
  switch (result.error._tag) {
    case 'NetworkError':
      break
    case 'ApiError':
      break
    case 'ValidationError':
      break
    case 'ConfigError':
      break
  }
}
```

### Error types

```typescript theme={null}
type ClientError = NetworkError | ApiError | ValidationError | ConfigError
```

#### NetworkError

Raised when a request fails before receiving a response.

```typescript theme={null}
interface NetworkError {
  readonly _tag: 'NetworkError'
  readonly code: NetworkErrorCode
  readonly message: string
  readonly retryAfterMs?: number
  readonly cause?: unknown
}

type NetworkErrorCode =
  | 'TIMEOUT'
  | 'CONNECTION_FAILED'
  | 'DNS_FAILED'
  | 'SSL_ERROR'
  | 'ABORTED'
  | 'OFFLINE'
```

#### ApiError

Raised when the Layers API returns an error response.

```typescript theme={null}
interface ApiError {
  readonly _tag: 'ApiError'
  readonly code: ApiErrorCode
  readonly source: 'layers'
  readonly message: string
  readonly status?: number
  readonly retryAfterMs?: number
  readonly cause?: unknown
}

type ApiErrorCode =
  | 'UNAUTHORIZED'
  | 'FORBIDDEN'
  | 'NOT_FOUND'
  | 'NOT_READY'
  | 'RATE_LIMITED'
  | 'SERVICE_UNAVAILABLE'
  | 'PARSE_ERROR'
  | 'UNKNOWN'
```

#### ValidationError

Raised when pre-request validation fails (for example, a required parameter is missing or out of range).

```typescript theme={null}
interface ValidationError {
  readonly _tag: 'ValidationError'
  readonly code: 'VALIDATION_FAILED'
  readonly message: string
  readonly operation: string
  readonly fields: ValidationFieldError[]
  readonly input?: unknown
}

interface ValidationFieldError {
  readonly field: string
  readonly code: 'REQUIRED_FIELD' | 'OUT_OF_RANGE'
  readonly message: string
  readonly value?: unknown
  readonly constraints?: { min?: number; max?: number }
}
```

#### ConfigError

Raised during `createClient()` when the SDK is misconfigured.

```typescript theme={null}
interface ConfigError {
  readonly _tag: 'ConfigError'
  readonly code: 'MISSING_CONFIG' | 'INVALID_CONFIG'
  readonly message: string
  readonly field: string
  readonly value?: unknown
  readonly expected?: string
}
```

### Retry helpers

`isRetryable` classifies errors by tag, code, and status. Use it standalone or as a `shouldRetry` predicate:

```typescript theme={null}
import { isRetryable } from '@commerce-blocks/sdk'

if (result.error && isRetryable(result.error)) {
  const delay = result.error.retryAfterMs ?? 1000
  setTimeout(() => collection.execute(), delay)
}
```

Retryable errors include `NetworkError` with code `TIMEOUT`, `CONNECTION_FAILED`, or `DNS_FAILED`, and `ApiError` with code `RATE_LIMITED` or `SERVICE_UNAVAILABLE`.

### Error type guard

`isClientError` can be used to check whether an unknown value is one of the SDK error types:

```typescript theme={null}
import { isClientError } from '@commerce-blocks/sdk'

try {
  // external code that may throw
} catch (e) {
  if (isClientError(e)) {
    console.error(e._tag, e.message)
  }
}
```

## Exported types

The SDK exports all types from `@commerce-blocks/sdk`:

```typescript theme={null}
import type {
  // Client
  Client,
  ClientConfig,
  CreateClientResult,

  // Controllers
  Controller,
  CollectionController,
  CollectionOptions,
  CollectionQuery,
  CollectionResult,
  SearchController,
  SearchQuery,
  SearchResult,
  SearchPrepareResult,
  SuggestController,
  SuggestOptions,
  BlocksController,
  BlocksOptions,
  BlocksQuery,
  BlocksResult,
  UploadImageController,
  UploadImageOptions,
  UploadImageResult,
  SearchByImageController,
  SearchByImageQuery,
  SearchContentController,
  SearchContentQuery,
  SearchContentResult,
  Article,

  // Context
  Context,
  GeoLocation,
  CartProduct,
  PriorSearch,
  Marketing,
  CustomerContext,

  // Product card
  ProductCardController,
  ProductCardOptions,
  ProductCardState,
  OptionGroup,
  OptionValue,
  OptionStatus,
  PriceData,
  PriceRangeData,

  // Product
  Product,
  ProductBase,
  ProductVariant,
  Image,
  Price,
  PriceRange,
  ProductOption,
  RichProductOption,
  Swatch,

  // State
  QueryState,
  QueryParams,
  RequestTransformer,
  Unsubscribe,

  // Errors
  ClientError,
  NetworkError,
  ApiError,
  ValidationError,
  ConfigError,

  // Filters
  FilterGroup,
  FilterExpression,

  // Signals
  Signal,
  ReadonlySignal,

  // Storage
  StorageAdapter,
  CacheOptions,

  // Fetch
  CustomFetch,
} from '@commerce-blocks/sdk'
```

## Next steps

* [Client methods](/sdk/api-reference/client-methods)
* [Filtering](/sdk/api-reference/filtering)
* [Cache and storage](/sdk/api-reference/cache-and-storage)
