supabase/supabase · error · Error

Content ${id} is not a notebook (got type: ${data.type})

Error message

Content ${id} is not a notebook (got type: ${data.type})

What it means

Thrown by getNotebook (apps/studio/data/content/notebooks/notebook-query.ts:20) after a successful getContentById call, when the returned content item's type is not 'notebook'. Unlike the ref/id guards, this fires after the fetch succeeds: it is a runtime type-narrowing failure, not a precondition. The api-types package does not yet include 'notebook' in the content type union (tracked by the ContentBase TODO), so the check is dynamic via `(data.type as string) !== 'notebook'`.

Source

Thrown at apps/studio/data/content/notebooks/notebook-query.ts:20

import { getContentById } from '../content-id-query'
import { contentKeys } from '../keys'
import type { Notebooks, ResponseError, UseCustomQueryOptions } from '@/types'

export type NotebookVariables = { projectRef?: string; id?: string }
export type NotebookError = ResponseError

export async function getNotebook(
  { projectRef, id }: NotebookVariables,
  signal?: AbortSignal,
  headers?: HeadersInit
) {
  const data = await getContentById({ projectRef, id }, signal, headers)

  // api-types doesn't have 'notebook' in GetUserContentByIdResponse['type'] yet — same gap
  // tracked by the ContentBase TODO in content-query.ts — so this narrowing can't be static.
  if ((data.type as string) !== 'notebook') {
    throw new Error(`Content ${id} is not a notebook (got type: ${data.type})`)
  }

  return data as unknown as Omit<typeof data, 'type' | 'content'> & {
    type: 'notebook'
    content: Notebooks.Content
  }
}

export type NotebookData = Awaited<ReturnType<typeof getNotebook>>

export const useNotebookQuery = <TData = NotebookData>(
  { projectRef, id }: NotebookVariables,
  { enabled = true, ...options }: UseCustomQueryOptions<NotebookData, NotebookError, TData> = {}
) =>
  useQuery<NotebookData, NotebookError, TData>({
    queryKey: contentKeys.resource(projectRef, id),
    queryFn: ({ signal }) => getNotebook({ projectRef, id }, signal),
    enabled: enabled && typeof projectRef !== 'undefined' && typeof id !== 'undefined',

View on GitHub (pinned to beee91b9c2)

Solutions

  1. Validate the content type before routing: fetch the item via getContentById and branch on data.type before entering the notebook view.
  2. In the notebook route, treat a non-notebook type as a 404/redirect to the correct viewer (SQL editor for 'sql', report viewer for 'report').
  3. Once api-types adds 'notebook' to the union, replace the dynamic cast with a static type guard.

Example fix

// before
const data = await getNotebook({ projectRef, id })

// after
const raw = await getContentById({ projectRef, id })
if ((raw.type as string) !== 'notebook') {
  // route to the correct viewer or show a not-found state
  return null
}
const data = raw as unknown as NotebookData
Defensive patterns

Strategy: type-guard

Validate before calling

const raw = await getContentById({ projectRef, id })
if ((raw.type as string) !== 'notebook') {
  // not a notebook — do not call getNotebook
  return null
}

Type guard

const isNotebook = (
  c: { type: string }
): c is { type: 'notebook' } => (c.type as string) === 'notebook'

Try / catch

try {
  const nb = await getNotebook({ projectRef, id })
} catch (e) {
  if (e instanceof Error && /is not a notebook/.test(e.message)) {
    // redirect to the correct viewer or show not-found
  } else {
    throw e
  }
}

Prevention

When it happens

Trigger: Loading a content id that exists but is a sql/report/log_sql snippet (not a notebook) through the notebook loader, e.g. a deep link to /notebook/{id} where the id actually belongs to a SQL snippet.

Common situations: Stale or shared URL pointing a notebook route at a non-notebook item, a content item whose type was changed, or the SQL editor and notebook viewer sharing an id namespace and routing incorrectly.

Related errors


AI-assisted analysis of supabase/supabase@beee91b9c2 (2026-08-12). Data as JSON: /api/errors/6e7de19c20df7959. Report an issue: GitHub.