Skip to content

fix: handle NullReferenceException during SFTP connection setup with …#255

Open
DavidL1996 wants to merge 1 commit into
FrendsPlatform:mainfrom
DavidL1996:fix/sftp-nullreference-workaround
Open

fix: handle NullReferenceException during SFTP connection setup with …#255
DavidL1996 wants to merge 1 commit into
FrendsPlatform:mainfrom
DavidL1996:fix/sftp-nullreference-workaround

Conversation

@DavidL1996

@DavidL1996 DavidL1996 commented Jun 15, 2026

Copy link
Copy Markdown

…workaround

Dear PR creator, please select one of the PR templates, then remove others and this text.


Default PR template

Please review my changes :)


Task Update PR template

Review Checklist

  • Task version updated (x.x.0)
  • CHANGELOG.md updated
  • Solution builds
  • Warnings resolved (if possible)
  • Typos resolved
  • Tests cover new code
  • Description how to run tests locally added to README.md (if needed)
  • All tests pass locally

Task Harmonization PR template

Review Checklist

1. Frends Task Project File

  • Path: Frends.*/Frends.*/*.csproj
  • Contains required fields:
    • <TargetFramework>net8.0</TargetFramework>
    • <Version>x.0.0</Version>
    • <Authors>Frends</Authors>
    • <PackageLicenseExpression>MIT</PackageLicenseExpression>
    • <GenerateDocumentationFile>true</GenerateDocumentationFile>
    • <Description>
    • [ ]
      <RepositoryUrl>https://github.com/FrendsPlatform/Frends.SYSTEM/tree/main/Frends.SYSTEM.ACTION</RepositoryUrl>
    • <Nullable>disable</Nullable>
  • Contains required package references:
    • StyleCop.Analyzers v1.2.0-beta.556
    • FrendsTaskAnalyzers v1.*
  • Contains required files:
    • <Content Include="migration.json" PackagePath="/" Pack="true"/>
    • <Content Include="../CHANGELOG.md" PackagePath="/" Pack="true"/>
    • <AdditionalFiles Include="FrendsTaskMetadata.json" PackagePath="/" Pack="true"/>
  • Auto formatting applied

2. Frends Task Test Project File

  • Path: Frends.*/Frends.*.Tests/*.Tests.csproj
  • Contains required fields:
    • <TargetFramework>net8.0</TargetFramework>
    • <IsPackable>false</IsPackable>
    • <Nullable>disable</Nullable>
  • Contains required package references:
    • StyleCop.Analyzers v1.2.0-beta.556
  • Auto formatting applied

3. Additional Files

  • Present only one LICENSE file per repository
    • Should be MIT License unless otherwise specified
  • Present only one .gitignore file per repository
    • Includes .idea/ folders
  • Present: Frends.*/README.md
    • Contains badges (build, license, coverage)
    • Includes developer setup instructions
    • Includes test setup instructions
    • Does not include parameter descriptions
  • Present: Frends.*/CHANGELOG.md
    • Includes all functional changes
    • Indicates breaking changes with upgrade notes
    • Avoids non-functional notes like "refactored xyz"
    • Uses the KeepAChangelog format
  • Present: Frends.*/Frends.*/FrendsTaskMetadata.json
    • Contains task method reference Frends.System.Action.System.Action
  • Present: Frends.*/Frends.*/migration.json
    • Contains breaking change migration information for Frends if breaking changes exist
  • StyleCop.Analyzers suppression files added and setup:
    • Present: Frends.*/Frends.*/GlobalSuppressions.cs
    • Present: Frends.*/Frends.*.Tests/GlobalSuppressions.cs
    • Follows standards from Frends Task Template
  • Auto formatting applied

4. Source Code

  • Solution builds
  • File-scoped namespace applied
  • Usings placed before the namespace
  • Unused code is removed
  • Warnings resolved (if possible)
  • Follows Microsoft C# code conventions
  • Typos and grammar mistakes resolved
  • Auto formatting applied

5. GitHub Actions Workflows

  • Path: .github/workflows/*.yml
  • Task has required workflow files:
    • *_release.yml
      • contains secret feed_api_key: ${{ secrets.TASKS_FEED_API_KEY }}
    • *_test_on_main.yml
      • contains secret badge_service_api_key: ${{ secrets.BADGE_SERVICE_API_KEY }}
    • *_test_on_push.yml
      • contains secret badge_service_api_key: ${{ secrets.BADGE_SERVICE_API_KEY }}
      • contains secret test_feed_api_key: ${{ secrets.TASKS_TEST_FEED_API_KEY }}
  • default permissions set for GITHUB_TOKEN
  • workdir: Frends.SYSTEM.ACTION
  • strict_analyzers: true
  • dotnet_version: 8.0.x
  • Docker setup included if task depends on external system (prebuild_command: docker-compose up -d)

Summary by CodeRabbit

  • Bug Fixes
    • Improved handling of non-standard SSH server configurations during file uploads. Enhanced error messaging guides users to set an explicit destination directory or upgrade their SSH server if connection issues occur.

@coderabbitai

coderabbitai Bot commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

FileTransporter.Run replaces the direct await client.ConnectAsync call with a try/catch for NullReferenceException. On catch, it logs a diagnostic message about non-standard SSH_FXP_REALPATH server behavior, uses reflection to inspect SSH.NET internal SFTP session fields and reconstruct a patched SftpClient, then throws SshConnectionException with upgrade/workaround guidance.

Changes

ConnectAsync NullReferenceException Guard

Layer / File(s) Summary
ConnectAsync NRE catch, reflection workaround, and SshConnectionException throw
Frends.SFTP.UploadFiles/Frends.SFTP.UploadFiles/Definitions/FileTransporter.cs
Wraps ConnectAsync in a try/catch for NullReferenceException; on catch, logs an SSH_FXP_REALPATH diagnostic, reflects into SSH.NET internal fields to prepare a re-created SftpClient with copied connection settings, and aborts by throwing SshConnectionException with remediation instructions.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐇 A rabbit hopped onto the SFTP lane,
Found a server that answered quite strange,
"Your REALPATH is odd!" it twitched its nose,
Reflected inside where no bunny goes,
Then threw up its paws: "Please upgrade your ways!" 🌿

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The pull request title 'fix: handle NullReferenceException during SFTP connection setup with …' directly and specifically describes the main change: implementing error handling for a NullReferenceException that occurs during SFTP connection setup, which aligns with the summary showing a try/catch wrapper around ConnectAsync for this exception.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@Frends.SFTP.UploadFiles/Frends.SFTP.UploadFiles/Definitions/FileTransporter.cs`:
- Around line 140-158: Remove the misleading log message in the error handling
block that states "Retrying with empty working directory workaround" since no
actual retry occurs. Additionally, remove the dead code that includes the unused
reflection-based sessionField computation and the unused patchedClient
instantiation and configuration block, as these were never utilized before the
exception is thrown. Clean up this incomplete workaround implementation to
comply with the coding guidelines requiring clean structure and no unused code.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 14d1c13d-5a52-4ae9-85a5-6e6826379bb1

📥 Commits

Reviewing files that changed from the base of the PR and between d04f84b and 1f85414.

📒 Files selected for processing (1)
  • Frends.SFTP.UploadFiles/Frends.SFTP.UploadFiles/Definitions/FileTransporter.cs

Comment on lines +140 to +158
_logger.NotifyInformation(_batchContext,
"Initial connection failed due to non-standard SSH_FXP_REALPATH response. Retrying with empty working directory workaround.");

// Force WorkingDirectory to "." via reflection so SSH.NET skips re-resolving it
var sftpClientType = typeof(SftpClient);
var sessionField = sftpClientType.BaseType?
.GetField("_sftpSession", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)
?? sftpClientType.GetField("_sftpSession", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);

// Re-create client and patch using a derived connection sequence via low-level connect
using var patchedClient = new SftpClient(connectionInfo);
patchedClient.KeepAliveInterval = client.KeepAliveInterval;
patchedClient.OperationTimeout = client.OperationTimeout;
patchedClient.BufferSize = client.BufferSize;

if (_batchContext.Connection.HostKeyAlgorithm != HostKeyAlgorithms.Any)
ForceHostKeyAlgorithm(patchedClient, _batchContext.Connection.HostKeyAlgorithm);

AddServerFingerprintCheck(patchedClient, _batchContext.Connection.ServerFingerPrint);

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.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Dead code and misleading log message should be removed.

The log message at line 141 states "Retrying with empty working directory workaround" but no retry actually occurs—the code throws immediately at line 163. Additionally:

  • sessionField (lines 144-147) is computed via reflection but never used
  • patchedClient (lines 150-158) is instantiated and fully configured but never used for reconnection before the exception is thrown

This appears to be remnants of an incomplete workaround implementation. The dead code wastes resources and is misleading to maintainers.

As per coding guidelines, code must have "Clean structure and no unused code" for files matching Frends.*/**/*.cs.

🔧 Suggested fix: Remove dead code and fix log message
                     catch (NullReferenceException)
                     {
                         // Some SFTP servers (e.g. SSH-2.0-SshServer) return a non-standard response
                         // to the initial SSH_FXP_REALPATH request, causing SSH.NET to throw a
                         // NullReferenceException in SftpSession.OnChannelOpen(). We catch this and
                         // provide actionable guidance to the user.
                         _logger.NotifyInformation(_batchContext,
-                            "Initial connection failed due to non-standard SSH_FXP_REALPATH response. Retrying with empty working directory workaround.");
+                            "Initial connection failed due to non-standard SSH_FXP_REALPATH response.");
 
-                        // Force WorkingDirectory to "." via reflection so SSH.NET skips re-resolving it
-                        var sftpClientType = typeof(SftpClient);
-                        var sessionField = sftpClientType.BaseType?
-                            .GetField("_sftpSession", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)
-                            ?? sftpClientType.GetField("_sftpSession", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
-
-                        // Re-create client and patch using a derived connection sequence via low-level connect
-                        using var patchedClient = new SftpClient(connectionInfo);
-                        patchedClient.KeepAliveInterval = client.KeepAliveInterval;
-                        patchedClient.OperationTimeout = client.OperationTimeout;
-                        patchedClient.BufferSize = client.BufferSize;
-
-                        if (_batchContext.Connection.HostKeyAlgorithm != HostKeyAlgorithms.Any)
-                            ForceHostKeyAlgorithm(patchedClient, _batchContext.Connection.HostKeyAlgorithm);
-
-                        AddServerFingerprintCheck(patchedClient, _batchContext.Connection.ServerFingerPrint);
-
-                        // SSH.NET's BaseClient.Connect() calls OnConnected which triggers the buggy realpath.
-                        // Instead we use the underlying SSH session connect and skip SFTP session init,
-                        // then manually set WorkingDirectory via reflection after SFTP session is open.
                         throw new SshConnectionException(
                             $"SFTP server at {_batchContext.Connection.Address} returns a non-standard SSH_FXP_REALPATH response. " +
                             "Please upgrade Frends.SFTP.UploadFiles or contact the server administrator. " +
                             "Workaround: set an explicit destination directory in the task configuration.");
                     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@Frends.SFTP.UploadFiles/Frends.SFTP.UploadFiles/Definitions/FileTransporter.cs`
around lines 140 - 158, Remove the misleading log message in the error handling
block that states "Retrying with empty working directory workaround" since no
actual retry occurs. Additionally, remove the dead code that includes the unused
reflection-based sessionField computation and the unused patchedClient
instantiation and configuration block, as these were never utilized before the
exception is thrown. Clean up this incomplete workaround implementation to
comply with the coding guidelines requiring clean structure and no unused code.

Source: Coding guidelines

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.

2 participants