Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions apps/docs/app/guides/ai-tools/ai-prompts/[slug]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import {
wrapInMarkdownCodeBlock,
} from '~/app/guides/getting-started/ai-prompts/[slug]/AiPrompts.utils'
import { GuideTemplate, newEditLink } from '~/features/docs/GuidesMdx.template'
import { notFoundWithPathname } from '~/features/docs/notFound.utils'
import { source } from 'common-tags'
import { notFound } from 'next/navigation'

export const dynamicParams = false

Expand All @@ -18,7 +18,7 @@ export default async function AiPromptsPage(props: { params: Promise<{ slug: str

const prompt = await getAiPrompt(slug)
if (!prompt) {
notFoundWithPathname(`/guides/ai-tools/ai-prompts/${slug}`)
notFound()
}

let { heading, content } = prompt
Expand Down
11 changes: 6 additions & 5 deletions apps/docs/app/guides/ai/python/[slug]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import { notFound } from 'next/navigation'
import { relative } from 'path'
import rehypeSlug from 'rehype-slug'

import { GuideTemplate, newEditLink } from '~/features/docs/GuidesMdx.template'
import { genGuideMeta, removeRedundantH1 } from '~/features/docs/GuidesMdx.utils'
import { notFoundWithPathname } from '~/features/docs/notFound.utils'
import { linkTransform, UrlTransformFunction } from '~/lib/mdx/plugins/rehypeLinkTransform'
import { getGitHubFileContents } from '~/lib/octokit'
import { UrlTransformFunction, linkTransform } from '~/lib/mdx/plugins/rehypeLinkTransform'
import remarkMkDocsAdmonition from '~/lib/mdx/plugins/remarkAdmonition'
import { removeTitle } from '~/lib/mdx/plugins/remarkRemoveTitle'
import { getGitHubFileContents } from '~/lib/octokit'
import { SerializeOptions } from '~/types/next-mdx-remote-serialize'
import rehypeSlug from 'rehype-slug'

export const dynamicParams = false

Expand Down Expand Up @@ -75,7 +76,7 @@ const getContent = async ({ slug }: Params) => {
const page = pageMap.find(({ slug: validSlug }) => validSlug && validSlug === slug)

if (!page) {
notFoundWithPathname(`/guides/ai/python/${slug}`)
notFound()
}

const { remoteFile, meta } = page
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import {
genGuidesStaticParams,
removeRedundantH1,
} from '~/features/docs/GuidesMdx.utils'
import { notFoundWithPathname } from '~/features/docs/notFound.utils'
import { newEditLink } from '~/features/helpers.edit-link'
import { Guide, GuideArticle, GuideFooter, GuideHeader, GuideMdxContent } from '~/features/ui/guide'
// End of third-party imports
Expand All @@ -21,6 +20,7 @@ import type { SerializeOptions } from '~/types/next-mdx-remote-serialize'
import { isFeatureEnabled } from 'common'
import matter from 'gray-matter'
import Link from 'next/link'
import { notFound } from 'next/navigation'
import rehypeSlug from 'rehype-slug'
import emoji from 'remark-emoji'
import { Button } from 'ui'
Expand Down Expand Up @@ -334,14 +334,11 @@ interface Params {
}

const WrappersDocs = async (props: { params: Promise<Params> }) => {
const params = await props.params

if (!isFeatureEnabled('docs:fdw')) {
notFoundWithPathname(
`/guides/database/extensions/wrappers${params.slug?.length ? `/${params.slug.join('/')}` : ''}`
)
notFound()
}

const params = await props.params
const { isExternal, meta, assetsBaseUrl, ...data } = await getContent(params)

// Create a combined URL transformer that handles both regular URLs and asset URLs
Expand Down
11 changes: 6 additions & 5 deletions apps/docs/app/guides/deployment/ci/[slug]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import { notFound } from 'next/navigation'
import { relative } from 'node:path'
import rehypeSlug from 'rehype-slug'

import { GuideTemplate, newEditLink } from '~/features/docs/GuidesMdx.template'
import { genGuideMeta, removeRedundantH1 } from '~/features/docs/GuidesMdx.utils'
import { notFoundWithPathname } from '~/features/docs/notFound.utils'
import { linkTransform, UrlTransformFunction } from '~/lib/mdx/plugins/rehypeLinkTransform'
import { getGitHubFileContents } from '~/lib/octokit'
import { UrlTransformFunction, linkTransform } from '~/lib/mdx/plugins/rehypeLinkTransform'
import remarkMkDocsAdmonition from '~/lib/mdx/plugins/remarkAdmonition'
import { removeTitle } from '~/lib/mdx/plugins/remarkRemoveTitle'
import remarkPyMdownTabs from '~/lib/mdx/plugins/remarkTabs'
import { getGitHubFileContents } from '~/lib/octokit'
import { SerializeOptions } from '~/types/next-mdx-remote-serialize'
import rehypeSlug from 'rehype-slug'

export const dynamicParams = false

Expand Down Expand Up @@ -74,7 +75,7 @@ const getContent = async ({ slug }: Params) => {
const page = pageMap.find(({ slug: validSlug }) => validSlug && validSlug === slug)

if (!page) {
notFoundWithPathname(`/guides/deployment/ci/${slug}`)
notFound()
}

const { remoteFile, meta } = page
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { GuideTemplate, newEditLink } from '~/features/docs/GuidesMdx.template'
import { genGuideMeta, removeRedundantH1 } from '~/features/docs/GuidesMdx.utils'
import { notFoundWithPathname } from '~/features/docs/notFound.utils'
import { getEmptyArray } from '~/features/helpers.fn'
import { IS_DEV } from '~/lib/constants'
import { isValidGuideFrontmatter } from '~/lib/docs'
Expand All @@ -11,6 +10,7 @@ import remarkPyMdownTabs from '~/lib/mdx/plugins/remarkTabs'
import { getGitHubFileContents } from '~/lib/octokit'
import { SerializeOptions } from '~/types/next-mdx-remote-serialize'
import matter from 'gray-matter'
import { notFound } from 'next/navigation'
import rehypeSlug from 'rehype-slug'

import {
Expand Down Expand Up @@ -106,7 +106,7 @@ const getContent = async ({ slug }: Params) => {
const page = pageMap.find((page) => page.slug === requestedSlug)

if (!page) {
notFoundWithPathname(`/guides/deployment/terraform${slug?.length ? `/${slug.join('/')}` : ''}`)
notFound()
}

const { meta, remoteFile, useRoot } = page
Expand Down
4 changes: 2 additions & 2 deletions apps/docs/app/guides/graphql/[[...slug]]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { isAbsolute, relative } from 'path'
import { GuideTemplate, newEditLink } from '~/features/docs/GuidesMdx.template'
import { genGuideMeta } from '~/features/docs/GuidesMdx.utils'
import { notFoundWithPathname } from '~/features/docs/notFound.utils'
import { getEmptyArray } from '~/features/helpers.fn'
import { IS_DEV } from '~/lib/constants'
import { linkTransform, UrlTransformFunction } from '~/lib/mdx/plugins/rehypeLinkTransform'
Expand All @@ -10,6 +9,7 @@ import { removeTitle } from '~/lib/mdx/plugins/remarkRemoveTitle'
import remarkPyMdownTabs from '~/lib/mdx/plugins/remarkTabs'
import { getGitHubFileContents } from '~/lib/octokit'
import { SerializeOptions } from '~/types/next-mdx-remote-serialize'
import { notFound } from 'next/navigation'
import rehypeSlug from 'rehype-slug'

// We fetch these docs at build time from an external repo
Expand Down Expand Up @@ -128,7 +128,7 @@ const getContent = async ({ slug }: Params) => {
const page = pageMap.find((page) => page.slug === slug?.at(0))

if (!page) {
notFoundWithPathname(`/guides/graphql${slug?.length ? `/${slug.join('/')}` : ''}`)
notFound()
}

const { remoteFile, meta } = page
Expand Down
5 changes: 3 additions & 2 deletions apps/docs/app/guides/troubleshooting/[slug]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { notFoundWithPathname } from '~/features/docs/notFound.utils'
import { notFound } from 'next/navigation'

import TroubleshootingPage from '~/features/docs/Troubleshooting.page'
import { getAllTroubleshootingEntries, getArticleSlug } from '~/features/docs/Troubleshooting.utils'
import { PROD_URL } from '~/lib/constants'
Expand All @@ -19,7 +20,7 @@ export default async function TroubleshootingEntryPage(props: {
const entry = allTroubleshootingEntries.find((entry) => getArticleSlug(entry) === slug)

if (!entry) {
notFoundWithPathname(`/guides/troubleshooting/${slug}`)
notFound()
}

return <TroubleshootingPage entry={entry} />
Expand Down
10 changes: 5 additions & 5 deletions apps/docs/app/reference/[...slug]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { notFound } from 'next/navigation'

import { REFERENCES } from '~/content/navigation.references'
import { notFoundWithPathname } from '~/features/docs/notFound.utils'
import { ApiReferencePage } from '~/features/docs/Reference.apiPage'
import { CliReferencePage } from '~/features/docs/Reference.cliPage'
import { ClientSdkReferencePage } from '~/features/docs/Reference.sdkPage'
Expand All @@ -17,10 +18,9 @@ export default async function ReferencePage(props: { params: Promise<{ slug: Arr
const params = await props.params

const { slug } = params
const referencePath = `/reference/${slug.join('/')}`

if (!Object.keys(REFERENCES).includes(slug[0].replaceAll('-', '_'))) {
notFoundWithPathname(referencePath)
notFound()
}

const parsedPath = parseReferencePath(slug)
Expand All @@ -34,7 +34,7 @@ export default async function ReferencePage(props: { params: Promise<{ slug: Arr

const sdkData = REFERENCES[sdkId]
if (sdkData.enabled === false) {
notFoundWithPathname(referencePath)
notFound()
}

const latestVersion = sdkData.versions[0]
Expand All @@ -52,7 +52,7 @@ export default async function ReferencePage(props: { params: Promise<{ slug: Arr
<SelfHostingReferencePage service={parsedPath.service} servicePath={parsedPath.servicePath} />
)
} else {
notFoundWithPathname(referencePath)
notFound()
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ const {
sdkKotlin: sdkKotlinEnabled,
sdkPython: sdkPythonEnabled,
sdkSwift: sdkSwiftEnabled,
docsAgentPlugin: agentPluginEnabled,
docsAgentSkills: agentSkillsEnabled,
docsPrompts: promptsEnabled,
} = isFeatureEnabled([
'authentication:show_providers',
'billing:all',
Expand All @@ -56,6 +59,9 @@ const {
'sdk:kotlin',
'sdk:python',
'sdk:swift',
'docs:agent_plugin',
'docs:agent_skills',
'docs:prompts',
])

const aiToolsEnabled = true
Expand Down Expand Up @@ -2395,6 +2401,7 @@ export const ai_tools: NavMenuConstant = {
{
name: 'Agent Plugin',
url: '/guides/ai-tools/plugins' as `/${string}`,
enabled: agentPluginEnabled,
},
{
name: 'MCP server',
Expand All @@ -2403,10 +2410,12 @@ export const ai_tools: NavMenuConstant = {
{
name: 'Agent Skills',
url: '/guides/ai-tools/ai-skills' as `/${string}`,
enabled: agentSkillsEnabled,
},
{
name: 'Prompts',
url: '/guides/ai-tools/ai-prompts' as `/${string}`,
enabled: promptsEnabled,
},

{
Expand Down
8 changes: 8 additions & 0 deletions apps/docs/content/guides/ai-tools/mcp.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,18 @@ After you log in, check that the MCP server is connected. For instance, in Curso

To verify the client has access to the MCP server tools, try asking it to query your project or database using natural language. For example: "What tables are there in the database? Use MCP tools."

<$Show if="docs:prompts">

For curated, ready-to-use prompts that work well with IDEs and AI agents, see our [AI Prompts](/docs/guides/getting-started/ai-prompts) collection.

</$Show>

<$Show if="docs:agent_skills">

Additionally, you can install Supabase agent skills alongside the MCP server, use the [Supabase Plugin for AI Coding Agents](/docs/guides/getting-started/plugins) for a combined one-step setup.

</$Show>

## Available tools

The Supabase MCP server provides tools organized into feature groups. All groups except Storage are enabled by default. You can enable or disable specific groups using the [configuration panel above](#step-2-configure-your-ai-tool).
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
---
title = "PostgREST error: 400 'column example_table.example_column does not exist' when using OR operators"
date_created = "2026-06-15T04:04:26+00:00"
topics = [ "platform", "api" ]
keywords = [ "postgrest", "column does not exist" ]
[[errors]]
http_status_code = 400
message = "column example_table.example_column does not exist"

---

If you receive a `400` error with the message `column example_table.example_column does not exist` only on mutation requests (`PATCH`, `POST`, `DELETE`) — while `SELECT` queries on the same column work fine — this is a known bug in PostgREST versions before 14.4 ([issue #3707](https://github.com/PostgREST/postgrest/issues/3707)).

## Root cause

The bug is triggered when an `or()` filter is included in a mutation request. PostgREST incorrectly applies the OR filter's column resolution logic to the mutation plan, causing it to fail to locate columns that exist and are otherwise accessible.

## How to confirm

1. **Reproduce the asymmetry** — run the same filter as a `GET` request. If it succeeds but the `PATCH`/`POST`/`DELETE` fails with the same column reference, the bug is the likely cause.
2. **Check your PostgREST version** — go to [Project Settings > Infrastructure](/dashboard/project/_/settings/infrastructure) and note the PostgreSQL version. PostgREST 14.1 and earlier are affected; 14.4+ includes the fix.
3. **Check Postgres logs** — if you have logging enabled, you should see `column "example_column" does not exist` errors correlating with the timestamps of the failed mutation requests.

## Resolution

### Option 1: Upgrade (permanent fix)

Navigate to [Project Settings > Infrastructure](/dashboard/project/_/settings/infrastructure) and upgrade to the latest Postgres version. This automatically upgrades PostgREST to 14.5+, where the bug is resolved.

### Option 2: Query workaround (immediate)

Explicitly include the column referenced in the error in the `select` parameter of your mutation request. This forces PostgREST to resolve the column correctly during the mutation plan.

For example, if your request looks like:

```
PATCH /rest/v1/example_table?or=(example_column.eq.value1,example_column.eq.value2)
```

Add `select=` with the affected column:

```
PATCH /rest/v1/example_table?or=(example_column.eq.value1,example_column.eq.value2)&select=id,example_column
```

This workaround applies per-request and does not require any schema changes or infrastructure modifications.
8 changes: 4 additions & 4 deletions apps/docs/features/docs/GuidesMdx.utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ import { fromMarkdown } from 'mdast-util-from-markdown'
import { gfmFromMarkdown } from 'mdast-util-gfm'
import { gfm } from 'micromark-extension-gfm'
import { type Metadata, type ResolvingMetadata } from 'next'
import { notFound } from 'next/navigation'

import { newEditLink } from './GuidesMdx.template'
import { checkGuidePageEnabled } from './NavigationPageStatus.utils'
import { notFoundWithPathname } from './notFound.utils'

const { metadataTitle } = getCustomContent(['metadata:title'])

Expand Down Expand Up @@ -56,7 +56,7 @@ const getGuidesMarkdownInternal = async (slug: string[]) => {
!fullPath.startsWith(GUIDES_DIRECTORY) ||
!PUBLISHED_SECTIONS.some((section) => relPath.startsWith(section))
) {
notFoundWithPathname(guidesPath)
notFound()
}

/**
Expand All @@ -65,7 +65,7 @@ const getGuidesMarkdownInternal = async (slug: string[]) => {
*/
if (!checkGuidePageEnabled(guidesPath)) {
console.log('Page is disabled: %s', guidesPath)
notFoundWithPathname(guidesPath)
notFound()
}

try {
Expand Down Expand Up @@ -100,7 +100,7 @@ const getGuidesMarkdownInternal = async (slug: string[]) => {
)
Sentry.captureException(error)
}
notFoundWithPathname(guidesPath)
notFound()
}
}

Expand Down
19 changes: 0 additions & 19 deletions apps/docs/features/docs/notFound.utils.ts

This file was deleted.

2 changes: 2 additions & 0 deletions apps/docs/public/humans.txt
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ Brendan Stephens
Brent Newson
Brian Cullen
Brent Schroeter
Bruno Kilian
Carel de Waal
Cameron Blackwood
Cameron Michie
Expand Down Expand Up @@ -206,6 +207,7 @@ Nick Littman
Nick Nowlan
Nicola Ferraro
Nicolas Nosenzo
Nik Richers
Noah Moss
Nyannyacha
Oli R
Expand Down
Loading
Loading