fix: add validation for resourceLocator properties in AI model nodes

This fixes a critical validation gap where AI agents could create invalid
configurations for nodes using resourceLocator properties (primarily AI model
nodes like OpenAI Chat Model v1.2+, Anthropic, Cohere, etc.).

Before this fix, AI agents could incorrectly pass a string value like:
  model: "gpt-4o-mini"

Instead of the required object format:
  model: { mode: "list", value: "gpt-4o-mini" }

These invalid configs would pass validation but fail at runtime in n8n.

Changes:
- Added resourceLocator type validation in config-validator.ts (lines 237-274)
- Validates value is an object with required 'mode' and 'value' properties
- Provides helpful error messages with exact fix suggestions
- Added 10 comprehensive test cases (100% passing)
- Updated version to 2.17.3
- Added CHANGELOG entry

Affected nodes: OpenAI Chat Model (v1.2+), Anthropic, Cohere, DeepSeek,
Groq, Mistral, OpenRouter, xAI Grok Chat Models, and embeddings nodes.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
czlonkowski
2025-10-07 16:54:29 +02:00
parent 3332eb09fc
commit e95ac7c335
6 changed files with 314 additions and 3 deletions

View File

@@ -234,8 +234,45 @@ export class ConfigValidator {
message: `Property '${key}' must be a boolean, got ${typeof value}`,
fix: `Change ${key} to true or false`
});
} else if (prop.type === 'resourceLocator') {
// resourceLocator must be an object with mode and value properties
if (typeof value !== 'object' || value === null || Array.isArray(value)) {
const fixValue = typeof value === 'string' ? value : JSON.stringify(value);
errors.push({
type: 'invalid_type',
property: key,
message: `Property '${key}' is a resourceLocator and must be an object with 'mode' and 'value' properties, got ${typeof value}`,
fix: `Change ${key} to { mode: "list", value: ${JSON.stringify(fixValue)} } or { mode: "id", value: ${JSON.stringify(fixValue)} }`
});
} else {
// Check required properties
if (!value.mode) {
errors.push({
type: 'missing_required',
property: `${key}.mode`,
message: `resourceLocator '${key}' is missing required property 'mode'`,
fix: `Add mode property: { mode: "list", value: ${JSON.stringify(value.value || '')} }`
});
} else if (typeof value.mode !== 'string') {
errors.push({
type: 'invalid_type',
property: `${key}.mode`,
message: `resourceLocator '${key}.mode' must be a string, got ${typeof value.mode}`,
fix: `Set mode to "list" or "id"`
});
}
if (value.value === undefined) {
errors.push({
type: 'missing_required',
property: `${key}.value`,
message: `resourceLocator '${key}' is missing required property 'value'`,
fix: `Add value property to specify the ${prop.displayName || key}`
});
}
}
}
// Options validation
if (prop.type === 'options' && prop.options) {
const validValues = prop.options.map((opt: any) =>