Skip to content

Fix dnx dispatcher to resolve install dir through symlinks - #54710

Merged
mthalman merged 2 commits into
dotnet:release/10.0.3xxfrom
mthalman:dev/mthalman/issue54687
Jun 15, 2026
Merged

Fix dnx dispatcher to resolve install dir through symlinks#54710
mthalman merged 2 commits into
dotnet:release/10.0.3xxfrom
mthalman:dev/mthalman/issue54687

Conversation

@mthalman

Copy link
Copy Markdown
Member

Tactics

Summary

On Linux, dnx invoked from PATH (the normal way) fails with The application to execute does not exist: '/usr/bin/sdk/<ver>/dotnet.dll'. The dispatcher script uses dirname "$0" to find its install directory, which returns /usr/bin when invoked through the /usr/bin/dnx symlink instead of the real install dir /usr/lib/dotnet. The fix resolves symlinks to find the script's real location. Two smaller fixes are bundled in: a clear error message when no SDK is installed (instead of a confusing .../sdk//dotnet.dll path-not-found), and using exec for the final dotnet invocation so signals and exit codes propagate correctly without an extra shell process.

Customer Impact

Every Linux user who runs dnx the normal way, via the dnx command on PATH, gets:

The application to execute does not exist: '/usr/bin/sdk/<sdk-version>/dotnet.dll'

dnx is unusable through the standard entry point. Only invocation by the absolute real path /usr/lib/dotnet/dnx works, and no normal user does that. macOS .pkg and tarball layouts that route through a symlink would be broken the same way. Additionally, users with no SDK installed (only the runtime) see the same dotnet.dll-not-found shape of error rather than a clear "dnx requires a .NET SDK" message.

Regression?

Yes, introduced by #54587. Prior to those PRs, dnx was a simple "$(dirname "$0")/dotnet" dnx "$@" wrapper. That older form also relies on dirname "$0", but it works through the /usr/bin/dnx symlink only by happy accident: the dotnet it dispatches to is /usr/bin/dotnet, which is itself a symlink to the real dotnet binary, and the script never needed to compute any explicit SDK path. #54587 introduced the explicit SDK_PATH construction, which is the first time the script needed the real install directory — and dirname "$0" doesn't give it. The bug has not yet shipped. It will appear in the next 10.0.302 servicing release unless this fix lands first.

Easy repro (in an ubuntu:24.04 container with dotnet-sdk-10.0 from the default repos):

docker run --rm -it -e DOTNET_NOLOGO=1 -e DOTNET_SKIP_FIRST_TIME_EXPERIENCE=true ubuntu:24.04 bash -c '
  set -e
  apt-get update -qq
  DEBIAN_FRONTEND=noninteractive apt-get install -y -qq dotnet-sdk-10.0 >/dev/null

  cat > /usr/lib/dotnet/dnx <<"EOF"
#!/bin/sh
DOTNET="$(dirname "$0")/dotnet"
SDK_VERSION=$("$DOTNET" --list-sdks | tail -1 | cut -d" " -f1)
SDK_PATH="$(dirname "$0")/sdk/$SDK_VERSION/dotnet.dll"
"$DOTNET" exec "$SDK_PATH" dnx "$@"
EOF
  chmod 755 /usr/lib/dotnet/dnx

  echo "--- via PATH (the bug) ---"
  dnx --help || true
  echo
  echo "--- direct invocation (works) ---"
  /usr/lib/dotnet/dnx --help | head -3
'

Output:

--- via PATH (the bug) ---
The application to execute does not exist: '/usr/bin/sdk/10.0.109/dotnet.dll'

--- direct invocation (works) ---
Description:
  Executes a tool from source without permanently installing it.

Testing

  • Manual testing:
    • POSIX shell test harness exercising the fixed script under sh, dash, bash, and busybox sh across multiple scenarios: direct invocation, single-symlink invocation, multi-hop symlink chain, PATH invocation, args with spaces / quotes / dollar signs, no-SDK error case, newest-SDK selection across multiple installed SDKs, paths with spaces, exit-code propagation.
    • shellcheck --shell=sh passes clean (POSIX-compliant).
    • End-to-end install validation in an Ubuntu 24.04 container: reproduced the bug, then swapped in the fixed dispatcher and confirmed dnx --help via /usr/bin/dnx, via PATH.
  • Follow-up: Add installer test that verifies dnx works from PATH after package install dotnet#7193 tracks adding a permanent installer-layer test that runs dnx.

Risk

Low. The change is confined to a single script. The symlink-resolution loop is the canonical POSIX idiom.

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

Fixes dnx launcher resolution on Unix-like installs where dnx is invoked via a PATH-reachable symlink (e.g., /usr/bin/dnx), ensuring the dispatcher computes the real install directory before locating dotnet and the newest SDK.

Changes:

  • Resolve the script’s real directory by following symlinks (without relying on readlink -f).
  • Emit a clear error when no SDK is installed (instead of constructing an invalid .../sdk//dotnet.dll path).
  • Use exec for the final dotnet exec ... invocation so signals and exit codes propagate correctly.

Comment thread src/Layout/redist/dnx Outdated
Replace the obsolescent `tail -1` shorthand with the POSIX-mandated
`tail -n 1` form. Every `tail` implementation we'd encounter (GNU,
BSD, macOS, busybox) supports both, but `-n N` is the form required by
POSIX and is preferred for portable `/bin/sh` scripts.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@mthalman

Copy link
Copy Markdown
Member Author

/ba-g unrelated failure

@mthalman
mthalman merged commit ab4fd30 into dotnet:release/10.0.3xx Jun 15, 2026
27 of 30 checks passed
@mthalman
mthalman deleted the dev/mthalman/issue54687 branch June 15, 2026 21:15
@dotnet-milestone-bot dotnet-milestone-bot Bot added this to the 11.0-preview7 milestone Jun 30, 2026
@lbussell

lbussell commented Jul 9, 2026

Copy link
Copy Markdown
Member

/backport to release/11.0.1xx-preview6

@github-actions

github-actions Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Started backporting to release/11.0.1xx-preview6 (link to workflow run)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants