supabase/supabase · error · ResponseError
body?.message
Error message
body?.message
What it means
Thrown when POST /api/ai/sql/title returns non-2xx while auto-generating a title for a SQL snippet. body?.message is forwarded to ResponseError (undefined → generic fallback); the real HTTP status is preserved as ResponseError.code.
Source
Thrown at apps/studio/data/ai/sql-title-mutation.ts:35
async function generateSqlTitle({ sql }: SqlTitleGenerateVariables) {
const url = `${BASE_PATH}/api/ai/sql/title-v2`
const headers = await constructHeaders({ 'Content-Type': 'application/json' })
const response = await fetchHandler(url, {
headers,
method: 'POST',
body: JSON.stringify({
sql,
}),
})
let body: any
try {
body = await response.json()
} catch {}
if (!response.ok) {
throw new ResponseError(body?.message, response.status)
}
return body as SqlTitleGenerateResponse
}
type SqlTitleGenerateData = Awaited<ReturnType<typeof generateSqlTitle>>
export const useSqlTitleGenerateMutation = ({
onSuccess,
onError,
...options
}: Omit<
UseCustomMutationOptions<SqlTitleGenerateData, ResponseError, SqlTitleGenerateVariables>,
'mutationFn'
> = {}) => {
return useMutation<SqlTitleGenerateData, ResponseError, SqlTitleGenerateVariables>({
mutationFn: (vars) => generateSqlTitle(vars),
async onError(data, variables, context) {View on GitHub (pinned to beee91b9c2)
Solutions
- Guard the mutation to require a non-empty `sql` string before submitting.
- Read ResponseError.code to branch behaviour (429 retry, 401 re-auth, 404 hide the feature).
- Provide a fallback title (e.g. 'Untitled snippet') when generation fails so the save flow is not blocked.
- Forward body.message but default to a readable string when absent.
Example fix
// before
if (!response.ok) {
throw new ResponseError(body?.message, response.status)
}
// after
if (!response.ok) {
throw new ResponseError(
body?.message || `Failed to generate title (HTTP ${response.status})`,
response.status
)
} Defensive patterns
Strategy: try-catch
Validate before calling
if (!sql || sql.trim().length === 0) {
throw new Error('Write some SQL before generating a title.')
} Type guard
function isRetryable(e: unknown): e is ResponseError {
return e instanceof ResponseError && (e.code === 429 || (e.code ?? 0) >= 500)
} Try / catch
try {
const { title } = await generateSqlTitle({ sql })
} catch (e) {
// fall back so the save flow is not blocked
setTitle('Untitled snippet')
toast.error(e instanceof Error ? e.message : 'Could not generate title')
} Prevention
- Require non-empty SQL before enabling the generate-title action.
- Fall back to a default title on failure so the save flow continues.
- Distinguish 401/429/404 by error.code.
When it happens
Trigger: Empty sql payload; AI service unavailable or timed out; rate-limited (429); session expired (401); route not deployed (404) in self-hosted builds; backend returns {error} instead of {message}.
Common situations: User saves a snippet with empty SQL; OpenAI quota exhausted; AI disabled; Cloudflare returns an HTML error page so the JSON parse is swallowed and body stays undefined.
Related errors
AI-assisted analysis of supabase/supabase@beee91b9c2 (2026-08-12).
Data as JSON: /api/errors/5fb02b24f06dc66f.
Report an issue: GitHub.