DYN-10661: never return a null templates directory - #17254
Conversation
PathManager.templatesDirectory had no initializer. Its only assignment was inside UpdatePreferenceItemPath, which returns early without assigning when PathHelper.CreateFolderIfNotExist rejects the location (unwritable %ProgramData%, unavailable network share, invalid path). A single failed call at startup therefore left PathManager .TemplatesDirectory null for the remainder of the session. In 3.6.2 that null flowed into path.Contains(TemplatesDirectory) in DynamoViewModel.InternalSaveAs, so String.Contains threw ArgumentNullException and Save, Save As and Ctrl+S all failed. PR DynamoDS#17190 replaced that call with the null-guarded IsPathInTemplateDirectoryTree, which stopped the exception but left the null in place - so the template write-protection added by that PR silently does nothing for affected users. Seed templatesDirectory with defaultTemplatesDirectory when common directories are built, so the property always reports a usable path and a rejected preference falls back instead of nulling out. Log the rejection to the Dynamo log as well; the silent failure is why this went unnoticed since the check was introduced in DynamoDS#14871. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
See the ticket for this pull request: https://jira.autodesk.com/browse/DYN-10661
There was a problem hiding this comment.
Pull request overview
This PR fixes a long-standing PathManager.TemplatesDirectory nullability issue by ensuring it always has a usable default value, even when a user-configured templates location cannot be created (e.g., locked-down %ProgramData%, invalid path, unavailable network share). This stabilizes downstream template-path checks used during Save/Save As and restores template-folder write protection behavior.
Changes:
- Seed
PathManager.templatesDirectorytoDefaultTemplatesDirectoryduring common directory initialization so it never remainsnull. - Log a fallback message when applying the preferred templates path fails during preference-location initialization.
- Add regression tests covering (1) non-null
TemplatesDirectoryon construction, (2) fallback behavior when the preferred location is unusable, and (3) template-path checks treating unknown template directories as “not in template tree”.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| test/DynamoCoreWpf3Tests/WorkspaceSaving.cs | Adds a regression test ensuring template-path checks don’t throw when the templates directory input is null/empty. |
| test/DynamoCoreTests/Configuration/PathManagerTests.cs | Adds unit tests to ensure TemplatesDirectory is never null and remains usable when a preferred location is rejected. |
| src/DynamoCore/Models/DynamoModel.cs | Logs when a configured templates location cannot be used and a fallback remains in effect. |
| src/DynamoCore/Configuration/PathManager.cs | Seeds templatesDirectory from defaultTemplatesDirectory during common directory construction. |
| Assert.That(updated, Is.False, "An unusable location should be rejected."); | ||
| Assert.That(pathManager.TemplatesDirectory, Is.Not.Null.And.Not.Empty, | ||
| "A rejected templates location must leave a usable fallback, not null."); |
There was a problem hiding this comment.
Good catch - fixed in fd921c7. The assertion now compares against pathManager.DefaultTemplatesDirectory rather than just checking for a non-empty value, so the test verifies the behavior its name claims.
Address review feedback: the test name promised a fallback to the default templates directory but the assertion only checked for a non-empty value, so it would have passed for any other non-default path. Compare against DefaultTemplatesDirectory instead. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|



Purpose
Fixes DYN-10661.
PathManager.templatesDirectoryhas no initializer. Its only assignment is insideUpdatePreferenceItemPath, which returns early without assigning whenPathHelper.CreateFolderIfNotExistrejects the location — an unwritable%ProgramData%, an unavailable network share, or an invalid path. A single failed call during startup therefore leavesPathManager.TemplatesDirectorynullfor the rest of the session.Two consequences:
path.Contains(Model.PathManager.TemplatesDirectory)inDynamoViewModel.InternalSaveAs, soString.ContainsthrewArgumentNullException(Value cannot be null. (Parameter 'value')) and Save / Save As / Ctrl+S all failed. Users could only save by closing the file and answering Yes to the save prompt — that route goes throughSaveAs(Guid, path), which never callsInternalSaveAs.IsPathInTemplateDirectoryTree, which stopped the exception but left the null in place. So the template-folder write-protection added by that PR silently does nothing for affected users, andSetDefaultInitialDirectoryquietly throws into its blanketcatchand falls back to Desktop.This PR fixes the cause rather than the symptom:
templatesDirectoryis seeded withdefaultTemplatesDirectorywhen common directories are built, so the property always reports a usable path and a rejected preference falls back instead of nulling out. The rejection is also logged — that silent failure is why this went unnoticed for two and a half years.Regression introduced in: #14871 (2024-01-27, "Open Dynamo Template as new workspace") introduced the unguarded
Containscall. #17190 (2026-06-23, DYN-10540) incidentally suppressed the exception but not the null.Fix targets: master only.
RC4.2.0_masteralready contains #17190, so theArgumentNullExceptionis not reachable there and no cherry-pick is required.Declarations
Check these if you believe they are true
Release Notes
Fixed an issue where Dynamo could fail to save a graph, or silently stop protecting the templates folder from being overwritten, when the configured template location was unavailable.
Reviewers
Roberto T (most recent owner of
PathManager)Testing notes — the
ArgumentNullExceptionitself is not reproducible on master (theIsPathInTemplateDirectoryTreeguard prevents it), so the new tests target the underlying null instead.PathManagerTestsfails on master before this change and passes after:WhenPathManagerIsConstructedThenTemplatesDirectoryIsNotNullWhenTemplateLocationCannotBeCreatedThenTemplatesDirectoryFallsBackToDefault— simulates a rejected location by squatting the target path with a file, the sameIOExceptionbranch a locked-down%ProgramData%or dead share takesTemplateSavePathCheckDoesNotThrowWhenTemplateDirectoryIsUnknownlocks in the guard so the 3.6.2 crash cannot return via a new caller.To verify by hand: point
<TemplateFilePath>inDynamoSettings.xmlat an extensionless file (e.g.C:\Temp\blockedTemplates), restart, then attempt Save As into the templates folder. Before this change the save is allowed; after it the save is blocked and the fallback is recorded indynamoLog.FYIs
Aabishkar KC (reporter), Neal Burnham