From 6bee3dfc6f26bb0c48af17238aafd551c4b8a4fd Mon Sep 17 00:00:00 2001 From: Rohit Sharma Date: Wed, 7 Jan 2026 20:16:27 +0530 Subject: [PATCH 1/5] Add Sentry error tracking for environment load/transform --- src/renderer/actions/local-sync/fs-utils.ts | 199 ++++++++++++++++---- 1 file changed, 158 insertions(+), 41 deletions(-) diff --git a/src/renderer/actions/local-sync/fs-utils.ts b/src/renderer/actions/local-sync/fs-utils.ts index 0dbc4373..7ea62eac 100644 --- a/src/renderer/actions/local-sync/fs-utils.ts +++ b/src/renderer/actions/local-sync/fs-utils.ts @@ -1,4 +1,5 @@ import { v4 as uuidv4 } from "uuid"; +import * as Sentry from "@sentry/browser"; import { API, APIEntity, @@ -275,9 +276,23 @@ export async function writeContent( }; } } - + const parsedContentResult = parseRaw(content, fileType.validator); if (parsedContentResult.type === "error") { + // Send analytics event for validation failure + Sentry.captureEvent({ + message: `Environment validation error: ${parsedContentResult.error.message}`, + level: "error", + tags: { + fileType: fileType.type, + errorType: "validation_failed", + }, + extra: { + path: resource.path, + error: parsedContentResult.error.message, + content, + }, + }); return createFileSystemError( { message: parsedContentResult.error.message }, resource.path, @@ -285,7 +300,6 @@ export async function writeContent( ); } - console.log("writing at", resource.path); const serializedContent = serializeContentForWriting(content); if (writeWithElevatedAccess) { await FsService.writeFileWithElevatedAccess( @@ -307,6 +321,20 @@ export async function writeContent( }, }; } catch (e: any) { + // Send analytics event for write exception + Sentry.captureEvent({ + message: `Environment write error: ${e.message}`, + level: "error", + tags: { + fileType: fileType.type, + errorType: "write_exception", + }, + extra: { + path: resource.path, + error: e.message, + stack: e.stack, + }, + }); return createFileSystemError(e, resource.path, fileType.type); } } @@ -1060,55 +1088,144 @@ export function sanitizeFsResourceList( export async function parseFileToEnv( file: FileResource ): Promise> { - const parsedFileResult = await parseFile({ - resource: file, - fileType: new EnvironmentRecordFileType(), - }); - - if (parsedFileResult.type === "error") { - return parsedFileResult; - } + try { + const parsedFileResult = await parseFile({ + resource: file, + fileType: new EnvironmentRecordFileType(), + }); - const { content } = parsedFileResult; + if (parsedFileResult.type === "error") { + Sentry.captureEvent({ + message: `Environment not found: ${parsedFileResult.error.message}`, + level: "error", + tags: { + fileType: "environment", + errorType: "environment_load_failed", + }, + extra: { + path: file.path, + error: parsedFileResult.error.message, + errorCode: parsedFileResult.error.code, + }, + }); - const isGlobal = file.path.endsWith(`/${GLOBAL_ENV_FILE}`); - const environment: Environment = { - type: "environment", - id: getIdFromPath(file.path), - name: content.name, - variables: content.variables, - isGlobal, - }; + return parsedFileResult; + } - const result: FileSystemResult = { - type: "success", - content: environment, - }; + const { content } = parsedFileResult; + const isGlobal = file.path.endsWith(`/${GLOBAL_ENV_FILE}`); + const environment: Environment = { + type: "environment", + id: getIdFromPath(file.path), + name: content.name, + variables: content.variables, + isGlobal, + }; - return result; + return { + type: "success", + content: environment, + }; + } catch (e: any) { + Sentry.captureEvent({ + message: `Environment load exception: ${e.message}`, + level: "error", + tags: { + fileType: "environment", + errorType: "environment_load_exception", + }, + extra: { + path: file.path, + error: e.message, + stack: e.stack, + }, + }); + return createFileSystemError(e, file.path, FileTypeEnum.ENVIRONMENT); + } } export function parseToEnvironmentEntity( variables: Record ) { - const newVariables: Record< - string, - Static<(typeof EnvironmentRecord)["variables"]> - > = {}; - // eslint-disable-next-line - for (const key in variables) { - newVariables[key] = { - value: variables[key].syncValue, - type: - variables[key].type === EnvironmentVariableType.Secret - ? EnvironmentVariableType.String - : variables[key].type, - id: variables[key].id, - isSecret: variables[key].type === EnvironmentVariableType.Secret, - }; - } + try { + const newVariables: Record< + string, + Static<(typeof EnvironmentRecord)["variables"]> + > = {}; + const missingFieldsPerVariable: Record = {}; + + // eslint-disable-next-line + for (const key in variables) { + const variable = variables[key]; + const missingFields: string[] = []; + + const resolvedValue = + variable.localValue !== undefined + ? variable.localValue + : variable.syncValue !== undefined + ? variable.syncValue + : (variable as any).value !== undefined + ? (variable as any).value + : undefined; + + if (!variable.id) { + missingFields.push("id"); + } + if (resolvedValue === undefined) { + missingFields.push("value"); + } + if (!variable.type) { + missingFields.push("type"); + } - return newVariables; + if (missingFields.length > 0) { + missingFieldsPerVariable[key] = missingFields; + } + + newVariables[key] = { + value: resolvedValue, + type: + variable.type === EnvironmentVariableType.Secret + ? EnvironmentVariableType.String + : variable.type, + id: variable.id, + isSecret: variable.type === EnvironmentVariableType.Secret, + }; + } + + // If any variables have missing fields, send analytics event + if (Object.keys(missingFieldsPerVariable).length > 0) { + Sentry.captureEvent({ + message: `Environment variables with missing fields: ${Object.keys(missingFieldsPerVariable).length}`, + level: "warning", + tags: { + fileType: "environment", + errorType: "invalid_variable_structure", + }, + extra: { + totalVariables: Object.keys(variables).length, + variablesWithIssues: Object.keys(missingFieldsPerVariable).length, + missingFields: missingFieldsPerVariable, + }, + }); + } + + return newVariables; + } catch (e: any) { + Sentry.captureEvent({ + message: `Environment variable transform exception: ${e.message}`, + level: "error", + tags: { + fileType: "environment", + errorType: "variable_transform_exception", + }, + extra: { + error: e.message, + stack: e.stack, + }, + }); + throw e; + } } export function getFileNameFromPath(filePath: string) { From 9f35636472269aa0e4125c7fa2350c49344cbf60 Mon Sep 17 00:00:00 2001 From: Rohit Sharma Date: Wed, 7 Jan 2026 20:35:10 +0530 Subject: [PATCH 2/5] Updated the changes to just include metadata --- src/renderer/actions/local-sync/fs-utils.ts | 37 ++++++++++++--------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/src/renderer/actions/local-sync/fs-utils.ts b/src/renderer/actions/local-sync/fs-utils.ts index 7ea62eac..e0d3a24b 100644 --- a/src/renderer/actions/local-sync/fs-utils.ts +++ b/src/renderer/actions/local-sync/fs-utils.ts @@ -279,9 +279,14 @@ export async function writeContent( const parsedContentResult = parseRaw(content, fileType.validator); if (parsedContentResult.type === "error") { - // Send analytics event for validation failure + // Send analytics event for validation failure (avoid sending raw content) + const contentMeta = + typeof content === "string" + ? { contentLength: content.length } + : { keys: Object.keys(content).length }; + Sentry.captureEvent({ - message: `Environment validation error: ${parsedContentResult.error.message}`, + message: `parseRaw() - Content validation error: ${parsedContentResult.error.message}`, level: "error", tags: { fileType: fileType.type, @@ -290,7 +295,7 @@ export async function writeContent( extra: { path: resource.path, error: parsedContentResult.error.message, - content, + contentMeta, }, }); return createFileSystemError( @@ -321,9 +326,9 @@ export async function writeContent( }, }; } catch (e: any) { - // Send analytics event for write exception + // Send analytics event for write exception (without raw content) Sentry.captureEvent({ - message: `Environment write error: ${e.message}`, + message: `writeContent() - File write error: ${e.message}`, level: "error", tags: { fileType: fileType.type, @@ -1196,7 +1201,7 @@ export function parseToEnvironmentEntity( // If any variables have missing fields, send analytics event if (Object.keys(missingFieldsPerVariable).length > 0) { Sentry.captureEvent({ - message: `Environment variables with missing fields: ${Object.keys(missingFieldsPerVariable).length}`, + message: `parseToEnvironmentEntityEnvironment() - variables with missing fields: ${Object.keys(missingFieldsPerVariable).length}`, level: "warning", tags: { fileType: "environment", @@ -1213,16 +1218,16 @@ export function parseToEnvironmentEntity( return newVariables; } catch (e: any) { Sentry.captureEvent({ - message: `Environment variable transform exception: ${e.message}`, - level: "error", - tags: { - fileType: "environment", - errorType: "variable_transform_exception", - }, - extra: { - error: e.message, - stack: e.stack, - }, + message: `parseToEnvironmentEntity() - Environment variable transform exception: ${e.message}`, + level: "error", + tags: { + fileType: "environment", + errorType: "variable_transform_exception", + }, + extra: { + error: e.message, + stack: e.stack, + }, }); throw e; } From cf5e853336f5b25ed82c99df0548b80f3c165719 Mon Sep 17 00:00:00 2001 From: Rohit Sharma Date: Wed, 7 Jan 2026 20:40:17 +0530 Subject: [PATCH 3/5] updated few checks --- src/renderer/actions/local-sync/fs-utils.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/renderer/actions/local-sync/fs-utils.ts b/src/renderer/actions/local-sync/fs-utils.ts index e0d3a24b..44bf7abd 100644 --- a/src/renderer/actions/local-sync/fs-utils.ts +++ b/src/renderer/actions/local-sync/fs-utils.ts @@ -1101,7 +1101,7 @@ export async function parseFileToEnv( if (parsedFileResult.type === "error") { Sentry.captureEvent({ - message: `Environment not found: ${parsedFileResult.error.message}`, + message: `parseFileToEnv() - Environment not found: ${parsedFileResult.error.message}`, level: "error", tags: { fileType: "environment", @@ -1133,7 +1133,7 @@ export async function parseFileToEnv( }; } catch (e: any) { Sentry.captureEvent({ - message: `Environment load exception: ${e.message}`, + message: `parseFileToEnv() - Environment load exception: ${e.message}`, level: "error", tags: { fileType: "environment", @@ -1173,7 +1173,7 @@ export function parseToEnvironmentEntity( ? (variable as any).value : undefined; - if (!variable.id) { + if (variable.id === undefined || variable.id === null) { missingFields.push("id"); } if (resolvedValue === undefined) { From dc31c457c661a5121cc36f73a26c9e2d7b2b80c5 Mon Sep 17 00:00:00 2001 From: Rohit Sharma Date: Wed, 7 Jan 2026 20:46:38 +0530 Subject: [PATCH 4/5] updated the minor warning --- src/renderer/actions/local-sync/fs-utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/renderer/actions/local-sync/fs-utils.ts b/src/renderer/actions/local-sync/fs-utils.ts index 44bf7abd..ab84d9d0 100644 --- a/src/renderer/actions/local-sync/fs-utils.ts +++ b/src/renderer/actions/local-sync/fs-utils.ts @@ -1201,7 +1201,7 @@ export function parseToEnvironmentEntity( // If any variables have missing fields, send analytics event if (Object.keys(missingFieldsPerVariable).length > 0) { Sentry.captureEvent({ - message: `parseToEnvironmentEntityEnvironment() - variables with missing fields: ${Object.keys(missingFieldsPerVariable).length}`, + message: `parseToEnvironmentEntity() - variables with missing fields: ${Object.keys(missingFieldsPerVariable).length}`, level: "warning", tags: { fileType: "environment", From 94bcff94b71db90c9bdbc0c4707c43c6188d0a7b Mon Sep 17 00:00:00 2001 From: Rohit Sharma Date: Wed, 7 Jan 2026 20:53:21 +0530 Subject: [PATCH 5/5] updated the message --- src/renderer/actions/local-sync/fs-utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/renderer/actions/local-sync/fs-utils.ts b/src/renderer/actions/local-sync/fs-utils.ts index ab84d9d0..6e32ba4f 100644 --- a/src/renderer/actions/local-sync/fs-utils.ts +++ b/src/renderer/actions/local-sync/fs-utils.ts @@ -1101,7 +1101,7 @@ export async function parseFileToEnv( if (parsedFileResult.type === "error") { Sentry.captureEvent({ - message: `parseFileToEnv() - Environment not found: ${parsedFileResult.error.message}`, + message: `parseFileToEnv() - Environment load failed: ${parsedFileResult.error.message}`, level: "error", tags: { fileType: "environment",