{"record":{"id":"5b41726021c83ea3","repo":"jgraph/drawio-desktop","slug":"bad-arg-fileobject-path","errorCode":null,"errorMessage":"bad arg: fileObject.path","messagePattern":"bad arg: fileObject\\.path","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/main/electron.js","lineNumber":3431,"sourceCode":"\t\t\t\t\tmodified: stat.mtimeMs,\n\t\t\t\t\tpath: bkpPaths[i]};\n\t\t}\n\t\tcatch (e){} // Ignore, try next prefix / no backup\n\t}\n\n\treturn null;\n};\n\nasync function saveFile(fileObject, data, origStat, overwrite, defEnc)\n{\n\tif (!checkFileContent(data))\n\t{\n\t\tthrow new Error('Invalid file data');\n\t}\n\n\tif (fileObject == null || typeof fileObject.path !== 'string')\n\t{\n\t\tthrow new Error('bad arg: fileObject.path');\n\t}\n\n\tawait assertWritablePath(fileObject.path);\n\n\tvar retryCount = 0;\n\tvar backupCreated = false;\n\tvar bkpPath = path.join(path.dirname(fileObject.path), BKP_PREFEX + path.basename(fileObject.path) + BKP_EXT);\n\tconst oldBkpPath = path.join(path.dirname(fileObject.path), OLD_BKP_PREFEX + path.basename(fileObject.path) + BKP_EXT);\n\tvar writeEnc = defEnc || fileObject.encoding;\n\n\t// Backup paths are derived siblings of fileObject.path, so they pass the\n\t// draft/bkp carve-out — but realpath them anyway in case symlinks have\n\t// been planted at those names.\n\tawait assertWritablePath(bkpPath);\n\n\tvar writeFile = async function()\n\t{\n\t\tlet fh;","sourceCodeStart":3413,"sourceCodeEnd":3449,"githubUrl":"https://github.com/jgraph/drawio-desktop/blob/403a2cb79f431db13a395f033b67eeebc1631754/src/main/electron.js#L3413-L3449","documentation":"saveFile's structural guard (src/main/electron.js:3429-3432): fileObject is null or its .path is not a string. This runs after the content check but before assertWritablePath, so a malformed fileObject never reaches fs APIs. The IPC handler already calls reqStr on fileObject.path, so this is a defence for internal callers of saveFile.","triggerScenarios":"saveFile invoked directly (not via the IPC switch) with fileObject = null, fileObject = {}, or fileObject.path = undefined/number.","commonSituations":"A refactor introduced a second call site for saveFile that skipped constructing a proper fileObject; a test harness passed a partial mock.","solutions":["Construct fileObject as {path: <string>, encoding?: <string>} before calling saveFile.","Route saves through the rendererReq 'saveFile' action so the IPC handler's reqStr guard catches the issue earlier.","Add a unit test that asserts saveFile throws this exact message for null fileObject."],"exampleFix":"// before\nawait saveFile({path: maybeUndefined}, data, stat, false);\n\n// after\nif (!fileObject || typeof fileObject.path !== 'string') throw new TypeError('fileObject.path required');\nawait saveFile(fileObject, data, stat, false);","handlingStrategy":"type-guard","validationCode":"function hasStringPath(fileObject) {\n\treturn fileObject != null && typeof fileObject.path === 'string' && fileObject.path.length > 0;\n}\nif (!hasStringPath(fileObject)) throw new TypeError('fileObject.path required');","typeGuard":"function hasStringPath(fileObject) {\n\treturn fileObject != null && typeof fileObject === 'object' &&\n\t\ttypeof fileObject.path === 'string' && fileObject.path.length > 0;\n}","tryCatchPattern":"try { await saveFile(fileObject, data, stat, false); }\ncatch (e) {\n\tif (e.message === 'bad arg: fileObject.path') { /* prompt user to pick a file */ return; }\n\tthrow e;\n}","preventionTips":["Always construct fileObject as {path: <string>} via a single factory.","Route saves through the IPC handler so reqStr guards .path first.","For internal saveFile callers, add a TS type or runtime contract on fileObject."],"tags":["save","validation","argument"],"backgroundTag":null,"analyzedSha":"403a2cb79f431db13a395f033b67eeebc1631754","analyzedAt":"2026-08-13T00:15:02.468Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}