{"record":{"id":"71e2d8d4b855a855","repo":"drawdb-io/drawdb","slug":"the-ai-import-produced-a-diagram-we-could-not-read","errorCode":null,"errorMessage":"The AI import produced a diagram we could not read. Please try again.","messagePattern":"The AI import produced a diagram we could not read\\. Please try again\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/utils/importAiDiagram.js","lineNumber":109,"sourceCode":"      warnings.push(\n        `Skipped relationship \"${relationship.name}\" because it did not resolve to a column.`,\n      );\n    }\n    return resolves;\n  });\n\n  const diagram = {\n    tables,\n    relationships: relationships.map((relationship, id) => ({\n      ...relationship,\n      id,\n    })),\n    enums,\n    types,\n  };\n\n  if (!jsonDiagramIsValid({ ...diagram, notes: [], subjectAreas: [] })) {\n    throw new Error(\n      \"The AI import produced a diagram we could not read. Please try again.\",\n    );\n  }\n\n  arrangeTables(diagram);\n\n  return { diagram, warnings };\n}\n","sourceCodeStart":91,"sourceCodeEnd":118,"githubUrl":"https://github.com/drawdb-io/drawdb/blob/e7086e7fc272b452706e7f402abc69c6b8384452/src/utils/importAiDiagram.js#L91-L118","documentation":"Thrown after normalization when the assembled diagram fails `jsonDiagramIsValid`, which runs the `jsonschema` Validator against `jsonSchema` (src/data/schemas.js). It means the AI payload had a non-empty `tables` array but the normalized shape still violates the JSON Schema contract — required table keys (id, name, x, y, fields, comment, indices, color), required field keys (id, name, type, default, check, primary, unique, notNull, increment, comment), the `^#[0-9a-fA-F]{6}$` color pattern, or required relationship keys. The single source site is src/utils/importAiDiagram.js:108.","triggerScenarios":"Any of: a table in `raw.tables` is missing `id`/`name`/`x`/`y`/`comment`/`indices`/`color`; `x`/`y` are non-numeric; `color` is absent or not a 6-digit hex; a field is missing `default`/`check`/`primary`/`unique`/`notNull`/`increment`/`comment`; a relationship is missing `startTableId`/`startFieldId`/`endTableId`/`endFieldId`/`name`/`cardinality`/`updateConstraint`/`deleteConstraint`. Note the table/field mapping at src/utils/importAiDiagram.js:73-80 only re-spreads each object and reassigns `type` — it does not default-fill the schema-required keys, so any AI omission flows straight into the validator.","commonSituations":"The AI omitted empty-string fields like `comment`/`check`/`default` (LLMs frequently drop keys whose value is empty); the AI returned tables without positional `x`/`y`; the AI omitted `indices` or `color`; a schema migration added a new required key (e.g. `uniqueConstraints`) that older prompts don't emit; a jsonschema version bump enforces stricter coercion (numbers vs numeric strings); the AI enumerated fields as a plain array of strings instead of objects.","solutions":["Capture `new Validator().validate(obj, jsonSchema).errors` and log it — it lists every offending property so you know exactly which required key or pattern failed.","Default-fill every schema-required key when mapping `raw.tables` and each `field` (the current mapping at lines 73-80 only reassigns `type`); enrich `raw` before calling normalizeAiDiagram or extend the mapping inside it.","Update the AI system prompt to include the exact JSON Schema (or a minimal required-keys example) so the model emits `comment`, `indices`, `color`, and full field objects.","Coerce types defensively: `Number(table.x ?? 0)`, `String(field.comment ?? \"\")`, and a hex fallback for `color`.","Pin the `jsonschema` package version and review `src/data/schemas.js` required arrays after any change — new required keys retroactively break existing AI output."],"exampleFix":"// before (src/utils/importAiDiagram.js:73-80) — only reassigns type, leaves required keys un-filled\nconst tables = raw.tables.map((table) => ({\n  ...table,\n  fields: (table.fields ?? []).map((field) => {\n    const { type, warning } = resolveType(field.type, validTypes, declaredNames);\n    if (warning) warnings.push(warning);\n    return { ...field, type };\n  }),\n}));\n\n// after — default-fill every schema-required table/field key\nconst HEX = /^#[0-9a-fA-F]{6}$/;\nconst tables = raw.tables.map((table) => ({\n  id: table.id ?? table.name,\n  name: String(table.name ?? \"Untitled\"),\n  x: Number(table.x ?? 0),\n  y: Number(table.y ?? 0),\n  comment: table.comment ?? \"\",\n  indices: table.indices ?? [],\n  color: HEX.test(table.color) ? table.color : \"#175e7a\",\n  ...table,\n  fields: (table.fields ?? []).map((field) => {\n    const { type, warning } = resolveType(field.type, validTypes, declaredNames);\n    if (warning) warnings.push(warning);\n    return {\n      id: field.id ?? field.name,\n      name: String(field.name ?? \"field\"),\n      default: field.default ?? \"\",\n      check: field.check ?? \"\",\n      primary: field.primary ?? false,\n      unique: field.unique ?? false,\n      notNull: field.notNull ?? false,\n      increment: field.increment ?? false,\n      comment: field.comment ?? \"\",\n      ...field,\n      type,\n    };\n  }),\n}));","handlingStrategy":"try-catch","validationCode":"// Pre-fill every schema-required key on each table/field so jsonDiagramIsValid passes.\n// Apply this to raw.tables BEFORE calling normalizeAiDiagram (or fold it into the mapping).\nimport { jsonDiagramIsValid } from \"../utils/validateSchema\";\n\nconst HEX = /^#[0-9a-fA-F]{6}$/;\n\nfunction normalizeTableShape(table) {\n  return {\n    id: table.id ?? table.name,\n    name: String(table.name ?? \"Untitled\"),\n    x: Number(table.x ?? 0),\n    y: Number(table.y ?? 0),\n    comment: table.comment ?? \"\",\n    indices: Array.isArray(table.indices) ? table.indices : [],\n    color: HEX.test(table.color) ? table.color : \"#175e7a\",\n    ...table,\n    fields: (table.fields ?? []).map((f) => ({\n      id: f.id ?? f.name,\n      name: String(f.name ?? \"field\"),\n      default: f.default ?? \"\",\n      check: f.check ?? \"\",\n      primary: f.primary ?? false,\n      unique: f.unique ?? false,\n      notNull: f.notNull ?? false,\n      increment: f.increment ?? false,\n      comment: f.comment ?? \"\",\n      ...f,\n    })),\n  };\n}\n\n// Usage:\nconst enriched = { ...result.diagram, tables: (result.diagram.tables ?? []).map(normalizeTableShape) };\nconst probe = { tables: enriched.tables, relationships: [], notes: [], subjectAreas: [], enums: [], types: [] };\nif (!jsonDiagramIsValid(probe)) {\n  // still invalid — surface a precise error instead of letting normalizeAiDiagram throw blindly\n  setError({ type: STATUS.ERROR, message: \"AI diagram shape rejected by schema.\" });\n  return;\n}\nconst { diagram, warnings } = normalizeAiDiagram(enriched, database);","typeGuard":"// Narrow a single table to the jsonSchema `tableSchema` required-key set.\nconst TABLE_REQUIRED = [\"id\", \"name\", \"x\", \"y\", \"fields\", \"comment\", \"indices\", \"color\"];\nconst FIELD_REQUIRED = [\"id\", \"name\", \"type\", \"default\", \"check\", \"primary\", \"unique\", \"notNull\", \"increment\", \"comment\"];\n\n/**\n * @param {unknown} t\n * @returns {t is Record<string, unknown>}\n */\nfunction tableMeetsSchema(t) {\n  if (!t || typeof t !== \"object\") return false;\n  const table = /** @type {any} */ (t);\n  if (!TABLE_REQUIRED.every((k) => Object.prototype.hasOwnProperty.call(table, k))) return false;\n  if (typeof table.x !== \"number\" || typeof table.y !== \"number\") return false;\n  if (!/^#[0-9a-fA-F]{6}$/.test(table.color)) return false;\n  return Array.isArray(table.fields) && table.fields.every((f) =>\n    FIELD_REQUIRED.every((k) => Object.prototype.hasOwnProperty.call(f, k))\n  );\n}","tryCatchPattern":"// Re-validate inside catch to extract the precise schema violations for diagnostics.\nimport { Validator } from \"jsonschema\";\nimport { jsonSchema } from \"../data/schemas\";\n\ntry {\n  const { diagram, warnings } = normalizeAiDiagram(raw, database);\n  // ...use diagram...\n} catch (e) {\n  if (e?.message?.startsWith(\"The AI import produced a diagram\")) {\n    const probe = { tables: raw.tables, relationships: raw.relationships ?? [], notes: [], subjectAreas: [], enums: raw.enums ?? [], types: raw.types ?? [] };\n    const result = new Validator().validate(probe, jsonSchema);\n    // result.errors is an array of { property, message, schema, argument }\n    console.error(\"AI diagram schema failures:\", result.errors);\n    setError({\n      type: STATUS.ERROR,\n      message: \"Could not read AI diagram. See console for the failing schema keys.\",\n    });\n    return;\n  }\n  throw e;\n}","preventionTips":["Default-fill every required key (including empty-string `comment`/`check`/`default` and `[]` for `indices`) when mapping AI output — LLMs routinely omit empty-valued keys.","On failure, log `new Validator().validate(obj, jsonSchema).errors` to identify the exact offending property instead of guessing.","Treat `src/data/schemas.js` `required` arrays as a breaking contract: review every AI prompt after adding a required key.","Pin the `jsonschema` package version; stricter coercion in newer versions can flip a previously-valid diagram to invalid.","Add unit tests for normalizeAiDiagram against minimal fixtures (table with only id/name/fields) so schema regressions surface in CI, not in production."],"tags":["ai-import","schema","validation","jsonschema","normalization"],"backgroundTag":null,"analyzedSha":"e7086e7fc272b452706e7f402abc69c6b8384452","analyzedAt":"2026-08-13T04:16:27.367Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}