PostgreSQL

Fix Prisma 42P01: table exists but raw query cannot find it

A catalog listing and a missing-table error can both be correct. Trace the name PostgreSQL resolves, preserve bound search values, and verify mapped tables.

Mahdi Ben RhoumaMahdi Ben Rhouma
7 min read

TL;DR#

If the table is named Item but Prisma's raw query says FROM item, PostgreSQL looks for the lowercase name, not the mixed-case table. Use FROM "Item", preserving the exact database spelling, and keep search values as ordinary tagged-template parameters. If the correctly quoted name still fails, check the schema and physical table name before changing migrations or connection settings.

The Stack Overflow report uses a similarity search against an Item table. A catalog query finds the table, but the raw search fails on Prisma 4.3.1:

text
Raw query failed. Code: `42P01`. Message: `table "item" does not exist`

That is a historical message from the report, not a claim that every current client produces that wording. PostgreSQL's error-code table identifies 42P01 as undefined_table. Prisma's P2010 error reference describes a raw query failure; read the database error inside it to find the failed object lookup.

Why it happens#

PostgreSQL's identifier rules distinguish unquoted names from double-quoted names. An unquoted name folds to lowercase. Therefore item and Item, when written without double quotes in SQL, resolve to the same lowercase identifier. A table created with the quoted name "Item" preserves the capital I and is a different object.

Prisma does not translate the table names written in a raw SQL statement into the names used by its model API. Its normal generated methods know the schema's mapping; raw SQL names the database objects directly. A model method succeeding while a raw query fails is therefore useful evidence, rather than a contradiction.

The catalog query in the question returns stored names. Finding the string Item there proves that a table with that spelling exists in the connected database. It does not prove that an unquoted FROM item will resolve to it, or that its schema is in the current search path.

A one-change reproduction#

The pattern was reproduced on 13 October 2026, while preparing this article, with Prisma Client and adapter-pg 7.10.0, pg 8.23.1, Node 22.23.3 and PostgreSQL 18.6 in Docker. The database held a quoted Item table with one seeded row, and the trigram extension was already available. The same Prisma connection could list that table and read it with the generated model method, while the unquoted raw query failed:

text
Raw query failed. Code: `42P01`. Message: `relation "item" does not exist`

Only the raw query's table identifier was changed to its double-quoted spelling. The search value, threshold, columns, database, schema, credentials and extension stayed unchanged. The corrected search returned the seeded row. The controls below also tested an unquoted capitalised name, a mapped model, a table outside the search path and an attempted table-name parameter.

The error metadata changed between the historical report and this tested adapter stack. The report places the database code directly in meta.code. Prisma 7.10.0 with adapter-pg instead returned it in the adapter error's cause, alongside the missing table name:

text
META {"driverAdapterError":{"name":"DriverAdapterError","cause":{"originalCode":"42P01","originalMessage":"relation \"item\" does not exist","kind":"TableDoesNotExist","table":"item"}}}

If your logging only reads the old metadata property, it can miss the useful database cause while still recording Prisma's outer P2010. Log the complete error during diagnosis and inspect the actual structure your installed client returns. The path shown here is an observed result for this version and adapter, not a promise about every Prisma configuration.

Fix#

  1. Inspect the exact name using the failing application's connection. Run this through the same Prisma instance as the search, rather than relying on another project's dashboard:

    typescript
    const tables = await prisma.$queryRaw<{
      schemaname: string;
      tablename: string;
    }[]>`
      SELECT schemaname, tablename
      FROM pg_catalog.pg_tables
      WHERE tablename IN ('Item', 'item', 'items')
      ORDER BY schemaname, tablename
    `;
    console.log(tables);

    Adapt the candidate names to the table you are diagnosing. These names are text values in a metadata query, so single quotes are correct here. For broader catalog and psql inspection, use the PostgreSQL table-listing reference. Copy the returned spelling and schema; do not lowercase a mixed-case result when writing the next query.

  2. Double-quote the existing mixed-case table name. For the Item table in the question, this is the fixed similarity search, using your application's already configured Prisma client:

    typescript
    async function findMatchingItems(search: string) {
      return prisma.$queryRaw`
        SELECT * FROM "Item"
        WHERE SIMILARITY(name, ${search}) > 0.4
           OR SIMILARITY(description, ${search}) > 0.4
      `;
    }

    There is no SQL string literal around the table identifier. Double quotes identify the table; single quotes denote text. The lowercase columns in this example need no case correction. If your actual columns are mixed-case too, inspect their stored names and quote those identifiers exactly as well.

  3. Keep the search value parameterised. The Prisma raw-query documentation explains that tagged-template variables represent data values, not table or column names. The search argument above is a value; the table is fixed SQL text. Interpolating a variable after FROM did not select the table in the reproduction: it failed with PostgreSQL syntax code 42601.

    Do not add single quotes around the search placeholder while repairing the identifier. That introduces a separate parameter-placement defect, covered by Prisma variables inside single quotes. There is also no reason to switch to a string-built Unsafe query for this fixed table name.

  4. Qualify the schema when quoting alone is insufficient. Check the connection context:

    typescript
    const context = await prisma.$queryRaw`
      SELECT current_database() AS database,
             current_schema() AS schema,
             current_setting('search_path') AS search_path
    `;
    console.log(context);

    PostgreSQL's schema documentation explains how an unqualified name is looked up through the search path. If the catalog identifies the table in public, the explicit form is:

    typescript
    const rows = await prisma.$queryRaw`
      SELECT * FROM public."Item"
    `;

    Use the schema returned by your catalog query. Quoting a wrong schema or connecting to a different database will not reveal a table that lives elsewhere. In the reproduction, a separate mixed-case table under an inventory schema failed when unqualified and succeeded when only its schema qualification was added.

Verify the fix#

First run the corrected search against a value known to exist. A non-empty result establishes more than the absence of an exception: it checks that the search reached the intended table and matched the expected data. If the table legitimately has no matching rows, compare with a simple read from the same qualified table before investigating search thresholds.

For a direct resolution check, PostgreSQL documents to_regclass as a lookup that returns null when the relation name is not found. The strings supplied to this function contain the identifier spelling you want PostgreSQL to resolve:

typescript
const lookup = await prisma.$queryRaw`
  SELECT to_regclass('item')::text AS unquoted,
         to_regclass('"Item"')::text AS quoted,
         to_regclass('public."Item"')::text AS qualified
`;
console.log(lookup);

With the reproduction's table, the lowercase lookup was null and both correctly quoted lookups resolved. Capitalising Item in unquoted SQL still failed. Quoting the real lowercase table items worked too, confirming that double quotes preserve spelling rather than automatically capitalising an identifier.

If your rewrite now fails at TypeScript compilation with a string not assignable to Sql, you have changed how the method is called. Continue with Prisma $queryRaw TS2345, which distinguishes a tagged template from a plain string passed in parentheses. That is a client call-shape error, separate from the database lookup fixed here.

One email a month — the latest explained errors

What the site published that month — mostly Next.js, Supabase and Node errors, explained. You confirm by email; unsubscribe any time in one click.

When the Prisma model and physical table have different names#

The Prisma database-mapping documentation describes the model mapping attribute. This schema declaration makes Item's generated client methods target a physical table called items:

prisma
model Item {
  id          Int    @id
  name        String
  description String
 
  @@map("items")
}

Raw SQL must use that physical name, not the model name. The reproduction checked this with a separate mapped model: its model method returned the seeded row, a raw query using the unmapped model name failed with 42P01, and the raw query using items succeeded.

Adding a mapping declaration is not the immediate fix for a database that already contains the quoted Item table. It changes which object the generated client expects; it does not, on its own, rename the existing database object. A naming change needs a deliberate schema and database migration. For the reported incident, correcting the raw SQL to match the existing table avoids that unnecessary change.

The search in the original report has one further prerequisite: PostgreSQL's pg_trgm extension provides similarity. After the name correction, a missing similarity function is a different failure. Removing only that extension from the throwaway database produced 42883; it did not reproduce the original missing-table error. Inspect extension availability and your deployment's migration process if that becomes the next message. Resolve the object named in each error in turn, keeping the database, physical table spelling and bound search values consistent.

Further reading: How to Get Enums in Prisma Client: Import, Query.

Still stuck? Paste your error.

Paste the error message or stack trace — we check it against every fix and tool on the site.

Matching happens in your browser. When you search, a cleaned error signature is sent so we can spot errors we do not cover yet — never the raw text, your IP address or your e-mail. Do not paste passwords or keys. Details

Written by

Mahdi Ben Rhouma
Mahdi Ben Rhouma

Full-Stack Dev — Next.js & Supabase

Solo developer building SaaS products with Next.js and Supabase. Writing about production patterns the official docs skip.

Remote