{"record":{"id":"b0d4f05f71fb2dc1","repo":"nocobase/nocobase","slug":"datasourcescollections-fields-apply-requires-datas","errorCode":null,"errorMessage":"dataSourcesCollections.fields:apply requires dataSourceKey","messagePattern":"dataSourcesCollections\\.fields:apply requires dataSourceKey","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/plugins/@nocobase/plugin-data-source-manager/src/server/services/external-field-apply.ts","lineNumber":73,"sourceCode":"      .map((name) => String(name).toLowerCase())\n      .sort()\n      .join('_'),\n  );\n}\n\nexport function normalizeExternalFieldInput(\n  rawValues: any,\n  defaults: {\n    dataSourceKey?: string;\n    collectionName?: string;\n  },\n) {\n  const values = mergeSettings(rawValues) as any;\n  const dataSourceKey = values.dataSourceKey || defaults.dataSourceKey;\n  const collectionName = values.collectionName || defaults.collectionName;\n\n  if (!dataSourceKey) {\n    throw new Error('dataSourcesCollections.fields:apply requires dataSourceKey');\n  }\n  if (!collectionName) {\n    throw new Error('dataSourcesCollections.fields:apply requires collectionName');\n  }\n  if (!values.name) {\n    throw new Error('dataSourcesCollections.fields:apply requires name');\n  }\n\n  const interfaceType = values.interface;\n  const type = values.type || RELATION_INTERFACE_TYPE_MAP[interfaceType];\n  const normalized = {\n    ...values,\n    dataSourceKey,\n    collectionName,\n    type,\n  };\n\n  if (RELATION_TYPES.has(normalized.type)) {","sourceCodeStart":55,"sourceCodeEnd":91,"githubUrl":"https://github.com/nocobase/nocobase/blob/fa42722fefe44265490dff2c27d79e2882bce4fa/packages/plugins/@nocobase/plugin-data-source-manager/src/server/services/external-field-apply.ts#L55-L91","documentation":"normalizeExternalFieldInput (used by the dataSourcesCollections.fields:apply flow for external data source fields) merges raw values with defaults and requires a dataSourceKey to know which data source the field belongs to. It throws when neither the submitted values nor the supplied defaults contain dataSourceKey.","triggerScenarios":"Calling applyExternalFieldDefinition(values, defaults) or the fields:apply action where values.dataSourceKey and defaults.dataSourceKey are both undefined/empty — e.g. applying a field to an external collection without passing { dataSourceKey } or including dataSourceKey in values.","commonSituations":"Calling applyExternalFieldDefinition directly from server code without the defaults argument; a client action targeting an external collection that omits the dataSourceKey from filterByTk/context; copy-pasted field-apply calls between main-app and external-source code paths.","solutions":["Pass dataSourceKey in the values payload: { dataSourceKey: 'main', collectionName: 'users', name: 'age', ... }.","Supply the defaults argument: applyExternalFieldDefinition(ctx, values, { dataSourceKey, collectionName }).","Derive the key from the qualified collection name ('dataSourceKey.collectionName') using parseCollectionNameWithDataSourceKey and pass it along."],"exampleFix":"// before\nawait applyExternalFieldDefinition(ctx, { collectionName: 'users', name: 'age', interface: 'number' });\n// after\nawait applyExternalFieldDefinition(ctx, { collectionName: 'users', name: 'age', interface: 'number' }, { dataSourceKey: 'main' });","handlingStrategy":"validation","validationCode":"function assertApplyInput(values: Record<string, unknown>, defaults: Record<string, unknown>) {\n  const merged = { ...defaults, ...values };\n  if (!merged.dataSourceKey) throw new Error('fields:apply: dataSourceKey missing (values or defaults)');\n}","typeGuard":"function hasFieldApplyScope(v: unknown): v is { dataSourceKey: string; collectionName: string; name: string } {\n  const o = v as any;\n  return typeof o?.dataSourceKey === 'string' && o.dataSourceKey.length > 0;\n}","tryCatchPattern":"try {\n  await applyExternalFieldDefinition(ctx, values, defaults);\n} catch (e) {\n  if (e.message.includes('requires dataSourceKey')) {\n    throw new Error(`Pass dataSourceKey via values or defaults; got values=${JSON.stringify(values)} defaults=${JSON.stringify(defaults)}`, { cause: e });\n  }\n  throw e;\n}","preventionTips":["Always pass the { dataSourceKey, collectionName } defaults argument from the caller's context.","Derive dataSourceKey from qualified collection names ('main.users') via parseCollectionNameWithDataSourceKey.","Keep dataSourceKey at the top level of the payload; a nested 'settings' object is only merged one level."],"tags":["validation","missing-parameter","external-data-source","nocobase"],"backgroundTag":"missing-required-argument","analyzedSha":"fa42722fefe44265490dff2c27d79e2882bce4fa","analyzedAt":"2026-09-01T00:54:31.202Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}