Skip to content

Commit ff57558

Browse files
roguliaclaude
andcommitted
fix: coerce node parameters to string before normalising
An n8n expression such as {{ $json.vatId }} resolves to undefined on an item without that field, so vatId and countryCode reached normalisation as undefined and crashed with "Cannot read properties of undefined (reading 'toUpperCase')" wrapped in a NodeApiError. Coerce both, guard the empty country code the same way the VAT number already was, and let input errors surface as NodeOperationError instead of being reported as API failures. Release 1.0.1. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1 parent 22f471c commit ff57558

3 files changed

Lines changed: 23 additions & 6 deletions

File tree

nodes/Vatnode/Vatnode.node.ts

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,14 @@ function normalizeVatId(vatId: string): string {
1919
return vatId.toUpperCase().replace(/[^A-Z0-9]/g, '');
2020
}
2121

22+
/**
23+
* An expression such as {{ $json.vatId }} resolves to undefined on an item that
24+
* lacks the field, so a parameter typed as string is not guaranteed to be one.
25+
*/
26+
function toStringParam(value: unknown): string {
27+
return value === undefined || value === null ? '' : String(value);
28+
}
29+
2230
export class Vatnode implements INodeType {
2331
description: INodeTypeDescription = {
2432
displayName: 'vatnode',
@@ -163,9 +171,9 @@ export class Vatnode implements INodeType {
163171
let result: unknown;
164172

165173
if (resource === 'vat' && operation === 'checkFormat') {
166-
result = checkFormat(this.getNodeParameter('vatId', i) as string);
174+
result = checkFormat(toStringParam(this.getNodeParameter('vatId', i)));
167175
} else if (resource === 'vat' && operation === 'validate') {
168-
const vatId = normalizeVatId(this.getNodeParameter('vatId', i) as string);
176+
const vatId = normalizeVatId(toStringParam(this.getNodeParameter('vatId', i)));
169177
if (vatId === '') {
170178
throw new NodeOperationError(this.getNode(), 'The VAT number is empty', {
171179
itemIndex: i,
@@ -178,7 +186,12 @@ export class Vatnode implements INodeType {
178186
};
179187
result = await this.helpers.httpRequestWithAuthentication.call(this, 'vatnodeApi', options);
180188
} else if (resource === 'rate' && operation === 'get') {
181-
const input = (this.getNodeParameter('countryCode', i) as string).trim().toUpperCase();
189+
const input = toStringParam(this.getNodeParameter('countryCode', i)).trim().toUpperCase();
190+
if (input === '') {
191+
throw new NodeOperationError(this.getNode(), 'The country code is empty', {
192+
itemIndex: i,
193+
});
194+
}
182195
// VIES writes Greece as EL, the rate endpoint keys it as GR.
183196
const countryCode = input === 'EL' ? 'GR' : input;
184197
result = await this.helpers.httpRequest({
@@ -217,6 +230,10 @@ export class Vatnode implements INodeType {
217230
});
218231
continue;
219232
}
233+
// Input problems are ours, not the API's — keep them out of NodeApiError.
234+
if (error instanceof NodeOperationError) {
235+
throw new NodeOperationError(this.getNode(), error.message, { itemIndex: i });
236+
}
220237
throw new NodeApiError(this.getNode(), error as JsonObject, { itemIndex: i });
221238
}
222239
}

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "n8n-nodes-vatnode",
3-
"version": "1.0.0",
3+
"version": "1.0.1",
44
"description": "Validate EU VAT numbers against VIES and look up EU VAT rates in n8n, powered by vatnode.",
55
"keywords": [
66
"n8n-community-node-package",

0 commit comments

Comments
 (0)