Skip to content

DYN-10661: never return a null templates directory - #17254

Open
jasonstratton wants to merge 2 commits into
DynamoDS:masterfrom
jasonstratton:DYN-10661-templates-directory-null
Open

DYN-10661: never return a null templates directory#17254
jasonstratton wants to merge 2 commits into
DynamoDS:masterfrom
jasonstratton:DYN-10661-templates-directory-null

Conversation

@jasonstratton

@jasonstratton jasonstratton commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Purpose

Fixes DYN-10661.

PathManager.templatesDirectory has no initializer. Its only assignment is inside UpdatePreferenceItemPath, which returns early without assigning when PathHelper.CreateFolderIfNotExist rejects the location — an unwritable %ProgramData%, an unavailable network share, or an invalid path. A single failed call during startup therefore leaves PathManager.TemplatesDirectory null for the rest of the session.

Two consequences:

  1. The reported crash (3.6.2). That null flowed into path.Contains(Model.PathManager.TemplatesDirectory) in DynamoViewModel.InternalSaveAs, so String.Contains threw ArgumentNullException (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 through SaveAs(Guid, path), which never calls InternalSaveAs.
  2. Still live on master. DYN-: Dyn 10540 template folder tree read only in dynamo #17190 replaced that call with the null-guarded 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, and SetDefaultInitialDirectory quietly throws into its blanket catch and falls back to Desktop.

This PR fixes the cause rather than the symptom: templatesDirectory is seeded 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. 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 Contains call. #17190 (2026-06-23, DYN-10540) incidentally suppressed the exception but not the null.

Fix targets: master only. RC4.2.0_master already contains #17190, so the ArgumentNullException is 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 ArgumentNullException itself is not reproducible on master (the IsPathInTemplateDirectoryTree guard prevents it), so the new tests target the underlying null instead. PathManagerTests fails on master before this change and passes after:

  • WhenPathManagerIsConstructedThenTemplatesDirectoryIsNotNull
  • WhenTemplateLocationCannotBeCreatedThenTemplatesDirectoryFallsBackToDefault — simulates a rejected location by squatting the target path with a file, the same IOException branch a locked-down %ProgramData% or dead share takes

TemplateSavePathCheckDoesNotThrowWhenTemplateDirectoryIsUnknown locks in the guard so the 3.6.2 crash cannot return via a new caller.

To verify by hand: point <TemplateFilePath> in DynamoSettings.xml at 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 in dynamoLog.

FYIs

Aabishkar KC (reporter), Neal Burnham

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>
Copilot AI review requested due to automatic review settings July 28, 2026 22:00

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See the ticket for this pull request: https://jira.autodesk.com/browse/DYN-10661

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.templatesDirectory to DefaultTemplatesDirectory during common directory initialization so it never remains null.
  • Log a fallback message when applying the preferred templates path fails during preference-location initialization.
  • Add regression tests covering (1) non-null TemplatesDirectory on 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.

Comment on lines +56 to +58
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.");

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>
Copilot AI review requested due to automatic review settings July 28, 2026 22:13

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated no new comments.

@sonarqubecloud

Copy link
Copy Markdown

@jasonstratton
jasonstratton requested review from a team and RobertGlobant20 July 29, 2026 03:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants