drawdb-io/drawdb · error · Error

The AI import returned no tables.

Error message

The AI import returned no tables.

What it means

Thrown by normalizeAiDiagram at the top of normalization as a hard precondition: the AI payload must be a non-null object whose `tables` is a non-empty array. It fails fast before any type/relationship resolution so downstream code never iterates an undefined or empty table list. The single source site is the guard `if (!raw || !Array.isArray(raw.tables) || !raw.tables.length)` at src/utils/importAiDiagram.js:59.

Source

Thrown at src/utils/importAiDiagram.js:60

  const declared = declaredNames.get(upper);
  if (declared) return { type: declared };

  for (const alias of TYPE_ALIASES[upper] ?? []) {
    if (validTypes[alias]) return { type: alias };
  }

  const fallback = FALLBACK_TYPES.find((candidate) => validTypes[candidate]);
  return {
    type: fallback ?? upper,
    warning: trimmed
      ? `Type "${trimmed}" is not available for this database, imported as ${fallback}.`
      : `A column had no type, imported as ${fallback}.`,
  };
}

export function normalizeAiDiagram(raw, database) {
  if (!raw || !Array.isArray(raw.tables) || !raw.tables.length) {
    throw new Error("The AI import returned no tables.");
  }

  const warnings = [];
  const validTypes = dbToTypes[database] || {};
  const enums = Array.isArray(raw.enums) ? raw.enums : [];
  const types = Array.isArray(raw.types) ? raw.types : [];

  const declaredNames = new Map();
  for (const entry of [...enums, ...types]) {
    if (entry?.name) declaredNames.set(String(entry.name).toUpperCase(), entry.name);
  }

  const tables = raw.tables.map((table) => ({
    ...table,
    fields: (table.fields ?? []).map((field) => {
      const { type, warning } = resolveType(field.type, validTypes, declaredNames);
      if (warning) warnings.push(warning);
      return { ...field, type };

View on GitHub (pinned to e7086e7fc2)

Solutions

  1. Log `result.diagram` (keys + `tables?.length`) right before the call in Modal.jsx:203 to see exactly what the AI returned.
  2. Add an explicit pre-check in `importSourceWithAi` and surface a user-facing 'AI returned no tables' error without invoking normalizeAiDiagram.
  3. Inspect the AI service call (`importSqlWithAi`) for non-2xx / empty-body / parse-failure paths that silently resolve to an object without `tables`.
  4. Tighten the system prompt sent to the AI to always emit `{ "tables": [ ... ], ... }` with at least one table, and validate the parsed JSON contract.
  5. Retry the AI request once on a detected empty payload before reporting a hard failure.

Example fix

// before
const { diagram, warnings } = normalizeAiDiagram(result.diagram, database);

// after
const raw = result?.diagram;
if (!raw || !Array.isArray(raw.tables) || raw.tables.length === 0) {
  setError({ type: STATUS.ERROR, message: "AI returned no tables. Refine the input and try again." });
  return;
}
const { diagram, warnings } = normalizeAiDiagram(raw, database);
Defensive patterns

Strategy: validation

Validate before calling

// Run this BEFORE normalizeAiDiagram to guarantee the precondition.
function hasAiTables(raw) {
  return (
    !!raw &&
    typeof raw === "object" &&
    Array.isArray(raw.tables) &&
    raw.tables.length > 0
  );
}

// In importSourceWithAi (Modal.jsx) before line 203:
const raw = result?.diagram;
if (!hasAiTables(raw)) {
  setError({
    type: STATUS.ERROR,
    message: "The AI service returned no tables. Refine the input and retry.",
  });
  return;
}
const { diagram, warnings } = normalizeAiDiagram(raw, database);

Type guard

// JSDoc-narrowed predicate (plain JS; works as a TS guard if you adopt TS).
/**
 * @param {unknown} raw
 * @returns {raw is { tables: unknown[]; enums?: unknown[]; types?: unknown[]; relationships?: unknown[] }}
 */
function isAiDiagramPayload(raw) {
  if (!raw || typeof raw !== "object") return false;
  const tables = /** @type {any} */ (raw).tables;
  return Array.isArray(tables) && tables.length > 0;
}

Try / catch

// Keep the existing try/catch in importSourceWithAi, but branch on this message
// so a missing-tables payload is reported cleanly instead of leaking the generic error.
try {
  const { diagram, warnings } = normalizeAiDiagram(result.diagram, database);
  // ...applyImportedDiagram(diagram)...
} catch (e) {
  if (e?.message === "The AI import returned no tables.") {
    setError({
      type: STATUS.ERROR,
      message: "The AI produced no tables. Adjust the prompt/SQL and try again.",
    });
    return;
  }
  throw e; // unknown error — let the outer handler decide
}

Prevention

When it happens

Trigger: Calling `normalizeAiDiagram(raw, database)` where (a) `raw` is null/undefined, (b) `raw.tables` is missing or not an array (e.g. an object, a string, a number), or (c) `raw.tables` is an array of length 0. In the app this is reached via Modal.jsx:203 as `normalizeAiDiagram(result.diagram, database)` after `importSqlWithAi(...)`, so any AI service response whose body lacks a populated `tables` field triggers it.

Common situations: The AI endpoint returned 200 but with an empty/malformed JSON body (e.g. `{}` or `{ "error": "..." }`); the model returned prose or a markdown code fence instead of a table array; an upstream JSON.parse collapsed the payload to null; the model hit a token/quota limit and emitted a truncated object; the import prompt was changed and no longer asks for a `tables` array; a retry returned a cached empty degenerate response.

Related errors


AI-assisted analysis of drawdb-io/drawdb@e7086e7fc2 (2026-08-13). Data as JSON: /api/errors/683a5a7f87593d72. Report an issue: GitHub.