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
- Validate the content type before routing: fetch the item via getContentById and branch on data.type before entering the notebook view.
- 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').
- 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
- Validate content type via getContentById before routing into the notebook viewer.
- Treat a non-notebook id on a notebook route as a redirect to the correct viewer (sql -> SQL editor).
- Once api-types adds 'notebook' to the content union, replace the dynamic cast with a static type guard.
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
- Project ref is required
- projectRef is required
- Project ref is required
- projectRef is required
- organizationSlug is required
AI-assisted analysis of supabase/supabase@beee91b9c2 (2026-08-12).
Data as JSON: /api/errors/6e7de19c20df7959.
Report an issue: GitHub.