{"record":{"id":"839cfa876a45dae4","repo":"twentyhq/twenty","slug":"graphql-errors-json-errors-map-e-e-message","errorCode":null,"errorMessage":"GraphQL errors: ${json.errors.map((e) => e.message).join('; ')}","messagePattern":"GraphQL errors: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/twenty-apps/internal/twenty-partners/src/scripts/configure-partner-rls.ts","lineNumber":141,"sourceCode":"async function metadataFetch<T>(\n  metadataUrl: string,\n  apiKey: string,\n  query: string,\n  variables?: Record<string, unknown>,\n): Promise<T> {\n  const res = await fetch(metadataUrl, {\n    method: 'POST',\n    headers: {\n      'Content-Type': 'application/json',\n      Authorization: `Bearer ${apiKey}`,\n    },\n    body: JSON.stringify({ query, variables }),\n  });\n\n  const json = (await res.json()) as MetadataResponse<T>;\n\n  if (json.errors && json.errors.length > 0) {\n    throw new Error(\n      `GraphQL errors: ${json.errors.map((e) => e.message).join('; ')}`,\n    );\n  }\n\n  return json.data;\n}\n\n// Pages through an object's fields until it finds a field with the given name.\n// Uses cursor-based pagination to avoid truncation on large objects (company/opportunity\n// have >200 fields, so a single 200-cap request may miss partnerUser).\nasync function findFieldByName(\n  metadataUrl: string,\n  apiKey: string,\n  objectId: string,\n  objectName: string,\n  fieldName: string,\n): Promise<string> {\n  let after: string | null = null;","sourceCodeStart":123,"sourceCodeEnd":159,"githubUrl":"https://github.com/twentyhq/twenty/blob/1f5dd2bbd2a8da3419c8cfd52dd545c0024df1a6/packages/twenty-apps/internal/twenty-partners/src/scripts/configure-partner-rls.ts#L123-L159","documentation":"configure-partner-rls's metadata fetch helper throws when the GraphQL response carries a non-empty errors array. It joins all error messages into one string. This surfaces server-side rejections from the metadata API (auth, query validation, permission) rather than treating them as silent no-ops.","triggerScenarios":"A POST to the metadata endpoint returns JSON with errors: [{ message }, ...]. Causes: TWENTY_PARTNERS_API_KEY invalid/expired (permission/authorization error); the query references a field/object not present in this workspace; metadata API version mismatch; malformed query.","commonSituations":"Wrong/rotated API key; workspace where the app isn't installed so metadata queries for partner objects fail; metadata API changed across server versions; copy-paste error in the query string.","solutions":["Read the joined messages — they name the exact GraphQL error (auth, field-not-found, etc.).","Verify TWENTY_PARTNERS_API_KEY is valid for the workspace and has metadata read access.","Confirm the app is installed and synced in the target workspace before running the script.","Reproduce the failing query in the GraphQL playground with the same key to see full error extensions."],"exampleFix":"// before\nif (json.errors && json.errors.length > 0) {\n  throw new Error(`GraphQL errors: ${json.errors.map((e) => e.message).join('; ')}`);\n}\n\n// after — include error codes/paths if present\nif (json.errors && json.errors.length > 0) {\n  const detail = json.errors.map((e) => `[${e.extensions?.code ?? '?'}] ${e.message} (path=${JSON.stringify(e.path)})`).join('; ');\n  throw new Error(`GraphQL errors: ${detail}`);\n}","handlingStrategy":"try-catch","validationCode":"import { config } from 'dotenv';\nconfig({ path: process.env.ENV_FILE ?? '.env.local' });\n\n// Pre-check the key is present and the URL reachable before the first metadata call.\nconst apiKey = process.env.TWENTY_PARTNERS_API_KEY;\nconst baseUrl = process.env.TWENTY_PARTNERS_API_URL?.replace(/\\/$/, '');\nif (!apiKey || !baseUrl) {\n  throw new Error('TWENTY_PARTNERS_API_URL and TWENTY_PARTNERS_API_KEY must be set');\n}","typeGuard":null,"tryCatchPattern":"try {\n  await configureRls();\n} catch (err) {\n  // The joined GraphQL messages name the exact server-side error.\n  // Common: authorization (bad key), field-not-found (metadata drift).\n  console.error('rls:configure failed:', err instanceof Error ? err.message : err);\n  process.exit(1);\n}","preventionTips":["Confirm TWENTY_PARTNERS_API_KEY is valid and has metadata read access before running.","Install and sync the app in the target workspace so metadata queries resolve.","Reproduce the failing metadata query in the GraphQL playground to read full error extensions.","Keep the script and the Twenty server version in sync to avoid query contract drift."],"tags":["graphql","metadata","config","rls","partners"],"backgroundTag":null,"analyzedSha":"1f5dd2bbd2a8da3419c8cfd52dd545c0024df1a6","analyzedAt":"2026-08-12T15:37:27.593Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}