mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-18 02:21:13 +00:00
feat(editor): Improve errors in output panel (#8644)
Co-authored-by: Michael Kret <michael.k@radency.com>
This commit is contained in:
@@ -3,10 +3,12 @@ import type {
|
||||
INodeExecutionData,
|
||||
INodeProperties,
|
||||
IExecuteFunctions,
|
||||
NodeApiError,
|
||||
} from 'n8n-workflow';
|
||||
import { updateDisplayOptions, wrapData } from '../../../../../utils/utilities';
|
||||
import { apiRequest } from '../../transport';
|
||||
import { baseRLC } from '../common.descriptions';
|
||||
import { processAirtableError } from '../../helpers/utils';
|
||||
|
||||
const properties: INodeProperties[] = [
|
||||
{
|
||||
@@ -45,6 +47,7 @@ export async function execute(
|
||||
|
||||
returnData.push(...executionData);
|
||||
} catch (error) {
|
||||
error = processAirtableError(error as NodeApiError, undefined, i);
|
||||
if (this.continueOnFail()) {
|
||||
returnData.push({ json: { error: error.message } });
|
||||
continue;
|
||||
|
||||
@@ -3,11 +3,12 @@ import type {
|
||||
INodeExecutionData,
|
||||
INodeProperties,
|
||||
IExecuteFunctions,
|
||||
NodeApiError,
|
||||
} from 'n8n-workflow';
|
||||
import { updateDisplayOptions, wrapData } from '../../../../../utils/utilities';
|
||||
import { apiRequest } from '../../transport';
|
||||
import { insertUpdateOptions } from '../common.descriptions';
|
||||
import { removeIgnored } from '../../helpers/utils';
|
||||
import { processAirtableError, removeIgnored } from '../../helpers/utils';
|
||||
|
||||
const properties: INodeProperties[] = [
|
||||
{
|
||||
@@ -85,6 +86,7 @@ export async function execute(
|
||||
|
||||
returnData.push(...executionData);
|
||||
} catch (error) {
|
||||
error = processAirtableError(error as NodeApiError, undefined, i);
|
||||
if (this.continueOnFail()) {
|
||||
returnData.push({ json: { message: error.message, error } });
|
||||
continue;
|
||||
|
||||
@@ -54,7 +54,7 @@ export async function execute(
|
||||
|
||||
returnData.push(...executionData);
|
||||
} catch (error) {
|
||||
error = processAirtableError(error as NodeApiError, id);
|
||||
error = processAirtableError(error as NodeApiError, id, i);
|
||||
if (this.continueOnFail()) {
|
||||
returnData.push({ json: { error: error.message } });
|
||||
continue;
|
||||
|
||||
@@ -90,7 +90,7 @@ export async function execute(
|
||||
|
||||
returnData.push(...executionData);
|
||||
} catch (error) {
|
||||
error = processAirtableError(error as NodeApiError, id);
|
||||
error = processAirtableError(error as NodeApiError, id, i);
|
||||
if (this.continueOnFail()) {
|
||||
returnData.push({ json: { error: error.message } });
|
||||
continue;
|
||||
|
||||
@@ -137,7 +137,7 @@ export async function execute(
|
||||
|
||||
returnData.push(...executionData);
|
||||
} catch (error) {
|
||||
error = processAirtableError(error as NodeApiError, recordId);
|
||||
error = processAirtableError(error as NodeApiError, recordId, i);
|
||||
if (this.continueOnFail()) {
|
||||
returnData.push({ json: { message: error.message, error } });
|
||||
continue;
|
||||
|
||||
@@ -3,10 +3,11 @@ import type {
|
||||
INodeExecutionData,
|
||||
INodeProperties,
|
||||
IExecuteFunctions,
|
||||
NodeApiError,
|
||||
} from 'n8n-workflow';
|
||||
import { updateDisplayOptions, wrapData } from '../../../../../utils/utilities';
|
||||
import { apiRequest, apiRequestAllItems, batchUpdate } from '../../transport';
|
||||
import { removeIgnored } from '../../helpers/utils';
|
||||
import { processAirtableError, removeIgnored } from '../../helpers/utils';
|
||||
import type { UpdateRecord } from '../../helpers/interfaces';
|
||||
import { insertUpdateOptions } from '../common.descriptions';
|
||||
|
||||
@@ -146,6 +147,7 @@ export async function execute(
|
||||
|
||||
returnData.push(...executionData);
|
||||
} catch (error) {
|
||||
error = processAirtableError(error as NodeApiError, undefined, i);
|
||||
if (this.continueOnFail()) {
|
||||
returnData.push({ json: { message: error.message, error } });
|
||||
continue;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { ApplicationError, type IDataObject, type NodeApiError } from 'n8n-workflow';
|
||||
import type { UpdateRecord } from './interfaces';
|
||||
import set from 'lodash/set';
|
||||
|
||||
export function removeIgnored(data: IDataObject, ignore: string | string[]) {
|
||||
if (ignore) {
|
||||
@@ -66,13 +67,18 @@ export function findMatches(
|
||||
}
|
||||
}
|
||||
|
||||
export function processAirtableError(error: NodeApiError, id?: string) {
|
||||
export function processAirtableError(error: NodeApiError, id?: string, itemIndex?: number) {
|
||||
if (error.description === 'NOT_FOUND' && id) {
|
||||
error.description = `${id} is not a valid Record ID`;
|
||||
}
|
||||
if (error.description?.includes('You must provide an array of up to 10 record objects') && id) {
|
||||
error.description = `${id} is not a valid Record ID`;
|
||||
}
|
||||
|
||||
if (itemIndex !== undefined) {
|
||||
set(error, 'context.itemIndex', itemIndex);
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
@@ -37,6 +37,7 @@ export async function calApiRequest(
|
||||
try {
|
||||
return await this.helpers.httpRequestWithAuthentication.call(this, 'calApi', options);
|
||||
} catch (error) {
|
||||
if (error instanceof NodeApiError) throw error;
|
||||
throw new NodeApiError(this.getNode(), error as JsonObject);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,8 @@ import { PythonSandbox } from './PythonSandbox';
|
||||
import { getSandboxContext } from './Sandbox';
|
||||
import { standardizeOutput } from './utils';
|
||||
|
||||
import set from 'lodash/set';
|
||||
|
||||
const { CODE_ENABLE_STDOUT } = process.env;
|
||||
|
||||
export class Code implements INodeType {
|
||||
@@ -133,7 +135,10 @@ export class Code implements INodeType {
|
||||
try {
|
||||
items = (await sandbox.runCodeAllItems()) as INodeExecutionData[];
|
||||
} catch (error) {
|
||||
if (!this.continueOnFail()) throw error;
|
||||
if (!this.continueOnFail()) {
|
||||
set(error, 'node', node);
|
||||
throw error;
|
||||
}
|
||||
items = [{ json: { error: error.message } }];
|
||||
}
|
||||
|
||||
@@ -158,7 +163,10 @@ export class Code implements INodeType {
|
||||
try {
|
||||
result = await sandbox.runCodeEachItem();
|
||||
} catch (error) {
|
||||
if (!this.continueOnFail()) throw error;
|
||||
if (!this.continueOnFail()) {
|
||||
set(error, 'node', node);
|
||||
throw error;
|
||||
}
|
||||
returnData.push({
|
||||
json: { error: error.message },
|
||||
pairedItem: {
|
||||
|
||||
@@ -86,6 +86,8 @@ export class FilterV2 implements INodeType {
|
||||
"Try to change the operator, switch ON the option 'Less Strict Type Validation', or change the type with an expression",
|
||||
);
|
||||
}
|
||||
set(error, 'context.itemIndex', itemIndex);
|
||||
set(error, 'node', this.getNode());
|
||||
throw error;
|
||||
}
|
||||
|
||||
|
||||
@@ -57,31 +57,13 @@ export async function googleApiRequest(
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
if (error instanceof NodeApiError) throw error;
|
||||
|
||||
if (error.code === 'ERR_OSSL_PEM_NO_START_LINE') {
|
||||
error.statusCode = '401';
|
||||
}
|
||||
|
||||
const apiError = new NodeApiError(
|
||||
this.getNode(),
|
||||
{
|
||||
reason: error.error,
|
||||
} as JsonObject,
|
||||
{ httpCode: String(error.statusCode) },
|
||||
);
|
||||
|
||||
if (
|
||||
apiError.message &&
|
||||
apiError.description &&
|
||||
(apiError.message.toLowerCase().includes('bad request') ||
|
||||
apiError.message.toLowerCase().includes('forbidden') ||
|
||||
apiError.message.toUpperCase().includes('UNKNOWN ERROR'))
|
||||
) {
|
||||
const message = apiError.message;
|
||||
apiError.message = apiError.description;
|
||||
apiError.description = message;
|
||||
}
|
||||
|
||||
throw apiError;
|
||||
throw new NodeApiError(this.getNode(), error as JsonObject);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { IExecuteFunctions, IDataObject, INodeExecutionData } from 'n8n-workflow';
|
||||
import { type IExecuteFunctions, type IDataObject, type INodeExecutionData } from 'n8n-workflow';
|
||||
import { GoogleSheet } from '../helpers/GoogleSheet';
|
||||
import { getSpreadsheetId } from '../helpers/GoogleSheets.utils';
|
||||
import type { GoogleSheets, ResourceLocator } from '../helpers/GoogleSheets.types';
|
||||
@@ -72,20 +72,11 @@ export async function router(this: IExecuteFunctions): Promise<INodeExecutionDat
|
||||
if (results?.length) {
|
||||
operationResult = operationResult.concat(results);
|
||||
}
|
||||
} catch (err) {
|
||||
} catch (error) {
|
||||
if (this.continueOnFail()) {
|
||||
operationResult.push({ json: this.getInputData(0)[0].json, error: err });
|
||||
operationResult.push({ json: this.getInputData(0)[0].json, error });
|
||||
} else {
|
||||
if (
|
||||
err.message &&
|
||||
(err.message.toLowerCase().includes('bad request') ||
|
||||
err.message.toLowerCase().includes('uknown error')) &&
|
||||
err.description
|
||||
) {
|
||||
err.message = err.description;
|
||||
err.description = undefined;
|
||||
}
|
||||
throw err;
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import { wrapData } from '../../../../../../utils/utilities';
|
||||
|
||||
export async function execute(
|
||||
this: IExecuteFunctions,
|
||||
sheet: GoogleSheet,
|
||||
_sheet: GoogleSheet,
|
||||
sheetName: string,
|
||||
): Promise<INodeExecutionData[]> {
|
||||
const returnData: INodeExecutionData[] = [];
|
||||
|
||||
@@ -139,11 +139,8 @@ export class GoogleSheet {
|
||||
});
|
||||
|
||||
if (!foundItem?.properties?.title) {
|
||||
throw new NodeOperationError(
|
||||
node,
|
||||
`Sheet with ${mode === 'name' ? 'name' : 'ID'} ${value} not found`,
|
||||
{ level: 'warning' },
|
||||
);
|
||||
const error = new Error(`Sheet with ${mode === 'name' ? 'name' : 'ID'} ${value} not found`);
|
||||
throw new NodeOperationError(node, error, { level: 'warning' });
|
||||
}
|
||||
|
||||
return foundItem.properties;
|
||||
|
||||
@@ -9,6 +9,7 @@ import type {
|
||||
} from 'n8n-workflow';
|
||||
import { NodeApiError } from 'n8n-workflow';
|
||||
import { getGoogleAccessToken } from '../../../GenericFunctions';
|
||||
import set from 'lodash/set';
|
||||
|
||||
export async function apiRequest(
|
||||
this: IExecuteFunctions | ILoadOptionsFunctions | IPollFunctions,
|
||||
@@ -62,11 +63,15 @@ export async function apiRequest(
|
||||
error.statusCode = '401';
|
||||
}
|
||||
|
||||
if (error.message.includes('PERMISSION_DENIED')) {
|
||||
const message = `Missing permissions for Google Sheet, ${error.message}}`;
|
||||
const details = error.description ? ` Details of the error: ${error.description}.` : '';
|
||||
const description = `Please check that the account you're using has the right permissions. (If you're trying to modify the sheet, you'll need edit access.)${details}`;
|
||||
throw new NodeApiError(this.getNode(), error as JsonObject, { message, description });
|
||||
if (error instanceof NodeApiError) {
|
||||
if (error.message.includes('PERMISSION_DENIED')) {
|
||||
const details = error.description ? ` Details of the error: ${error.description}.` : '';
|
||||
const description = `Please check that the account you're using has the right permissions. (If you're trying to modify the sheet, you'll need edit access.)${details}`;
|
||||
|
||||
set(error, 'description', description);
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
|
||||
throw new NodeApiError(this.getNode(), error as JsonObject);
|
||||
|
||||
@@ -34,6 +34,7 @@ import {
|
||||
sanitizeUiMessage,
|
||||
} from '../GenericFunctions';
|
||||
import { keysToLowercase } from '@utils/utilities';
|
||||
import set from 'lodash/set';
|
||||
|
||||
function toText<T>(data: T) {
|
||||
if (typeof data === 'object' && data !== null) {
|
||||
@@ -1255,6 +1256,7 @@ export class HttpRequestV3 implements INodeType {
|
||||
requestInterval: number;
|
||||
};
|
||||
|
||||
const sanitazedRequests: IDataObject[] = [];
|
||||
for (let itemIndex = 0; itemIndex < items.length; itemIndex++) {
|
||||
if (authentication === 'genericCredentialType') {
|
||||
genericCredentialType = this.getNodeParameter('genericAuthType', 0) as string;
|
||||
@@ -1627,8 +1629,11 @@ export class HttpRequestV3 implements INodeType {
|
||||
'application/json,text/html,application/xhtml+xml,application/xml,text/*;q=0.9, image/*;q=0.8, */*;q=0.7';
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
this.sendMessageToUI(sanitizeUiMessage(requestOptions, authDataKeys));
|
||||
const sanitazedRequestOptions = sanitizeUiMessage(requestOptions, authDataKeys);
|
||||
this.sendMessageToUI(sanitazedRequestOptions);
|
||||
sanitazedRequests.push(sanitazedRequestOptions);
|
||||
} catch (e) {}
|
||||
|
||||
if (pagination && pagination.paginationMode !== 'off') {
|
||||
@@ -1770,7 +1775,9 @@ export class HttpRequestV3 implements INodeType {
|
||||
if (autoDetectResponseFormat && responseData.reason.error instanceof Buffer) {
|
||||
responseData.reason.error = Buffer.from(responseData.reason.error as Buffer).toString();
|
||||
}
|
||||
throw new NodeApiError(this.getNode(), responseData as JsonObject, { itemIndex });
|
||||
const error = new NodeApiError(this.getNode(), responseData as JsonObject, { itemIndex });
|
||||
set(error, 'context.request', sanitazedRequests[itemIndex]);
|
||||
throw error;
|
||||
} else {
|
||||
removeCircularRefs(responseData.reason as JsonObject);
|
||||
// Return the actual reason as error
|
||||
|
||||
@@ -15,7 +15,7 @@ import type {
|
||||
INodeTypeDescription,
|
||||
JsonObject,
|
||||
} from 'n8n-workflow';
|
||||
import { NodeOperationError } from 'n8n-workflow';
|
||||
import { NodeApiError, NodeOperationError } from 'n8n-workflow';
|
||||
|
||||
import { snakeCase } from 'change-case';
|
||||
import { generatePairedItemData } from '../../../utils/utilities';
|
||||
@@ -47,6 +47,8 @@ import type { IForm } from './FormInterface';
|
||||
|
||||
import type { IAssociation, IDeal } from './DealInterface';
|
||||
|
||||
import set from 'lodash/set';
|
||||
|
||||
export class HubspotV2 implements INodeType {
|
||||
description: INodeTypeDescription;
|
||||
|
||||
@@ -3058,21 +3060,17 @@ export class HubspotV2 implements INodeType {
|
||||
error.cause.error?.validationResults &&
|
||||
error.cause.error.validationResults[0].error === 'INVALID_EMAIL'
|
||||
) {
|
||||
throw new NodeOperationError(
|
||||
this.getNode(),
|
||||
error.cause.error.validationResults[0].message as string,
|
||||
);
|
||||
const message = error.cause.error.validationResults[0].message as string;
|
||||
set(error, 'message', message);
|
||||
}
|
||||
if (error.cause.error?.message !== 'The resource you are requesting could not be found') {
|
||||
if (error.httpCode === '404' && error.description === 'resource not found') {
|
||||
throw new NodeOperationError(
|
||||
this.getNode(),
|
||||
`${error.node.parameters.resource} #${
|
||||
error.node.parameters[`${error.node.parameters.resource}Id`].value
|
||||
} could not be found. Check your ${error.node.parameters.resource} ID is correct`,
|
||||
);
|
||||
const message = `${error.node.parameters.resource} #${
|
||||
error.node.parameters[`${error.node.parameters.resource}Id`].value
|
||||
} could not be found. Check your ${error.node.parameters.resource} ID is correct`;
|
||||
|
||||
set(error, 'message', message);
|
||||
}
|
||||
throw new NodeOperationError(this.getNode(), error as string);
|
||||
}
|
||||
if (this.continueOnFail()) {
|
||||
returnData.push({
|
||||
@@ -3081,6 +3079,9 @@ export class HubspotV2 implements INodeType {
|
||||
});
|
||||
continue;
|
||||
}
|
||||
if (error instanceof NodeApiError) {
|
||||
set(error, 'context.itemIndex', i);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,6 +86,8 @@ export class IfV2 implements INodeType {
|
||||
"Try to change the operator, switch ON the option 'Less Strict Type Validation', or change the type with an expression",
|
||||
);
|
||||
}
|
||||
set(error, 'context.itemIndex', itemIndex);
|
||||
set(error, 'node', this.getNode());
|
||||
throw error;
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ import type {
|
||||
} from 'n8n-workflow';
|
||||
import { NodeConnectionType, NodeOperationError } from 'n8n-workflow';
|
||||
import { capitalize } from '@utils/utilities';
|
||||
import set from 'lodash/set';
|
||||
|
||||
const configuredOutputs = (parameters: INodeParameters) => {
|
||||
const mode = parameters.mode as string;
|
||||
@@ -351,6 +352,8 @@ export class SwitchV3 implements INodeType {
|
||||
error.description =
|
||||
"Try to change the operator, switch ON the option 'Less Strict Type Validation', or change the type with an expression";
|
||||
}
|
||||
set(error, 'context.itemIndex', itemIndex);
|
||||
set(error, 'node', this.getNode());
|
||||
throw error;
|
||||
}
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@ export async function uprocApiRequest(
|
||||
try {
|
||||
return await this.helpers.httpRequestWithAuthentication.call(this, 'uprocApi', options);
|
||||
} catch (error) {
|
||||
if (error instanceof NodeApiError) throw error;
|
||||
throw new NodeApiError(this.getNode(), error as JsonObject);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user