DEV Community

Cover image for Troubleshooting .NET MAUI on macOS: From Build Errors to Device Debugging
SANKET PATIL
SANKET PATIL

Posted on AI-assisted

Troubleshooting .NET MAUI on macOS: From Build Errors to Device Debugging

Part 2 of 2 - Troubleshooting the development environment

In Part 1, I covered how I approached setting up a reliable .NET MAUI development environment on macOS.

This article focuses on what happened when things didn't work as expected.

The interesting part of setting up a development machine isn't always installing the tools. Sometimes the tools are installed, the expected commands exist, and everything looks correct - but the actual application still cannot build or run.

That's where troubleshooting becomes important.

During my setup, I encountered several situations where the first assumption wasn't the actual cause of the problem.

That led me to a simple principle:

Don't troubleshoot based only on what is installed. Troubleshoot based on what the application and build tools can actually see and use.


Table of Contents


1. Start With the Error, Not the Installation

When a development environment isn't working, the natural reaction is often:

"Something isn't installed correctly."

Sometimes that's true.

But reinstalling everything isn't necessarily the best first step.

A better starting point is the actual error.

Think about the environment as a dependency chain:

Project
   ↓
.NET SDK
   ↓
.NET MAUI workload
   ↓
Platform tooling
   ↓
Build tools
   ↓
Simulator / Device
   ↓
Application
Enter fullscreen mode Exit fullscreen mode

If the application fails somewhere in that chain, the error usually gives you a starting point.

For example:

iOS build failure
        ↓
   Check Xcode
        ↓
Check selected developer directory
        ↓
Check .NET / MAUI workload
        ↓
Check compatibility
Enter fullscreen mode Exit fullscreen mode

Or:

Android build failure
        ↓
Check Android SDK
        ↓
    Check JDK
        ↓
Check JDK discovery
        ↓
Check build configuration
Enter fullscreen mode Exit fullscreen mode

The goal is to narrow the problem down before changing anything.

A useful rule

Find the failing layer before trying to fix the environment.

This sounds obvious, but it can prevent a lot of unnecessary reinstallations.


2. Xcode Is Installed - So Why Isn't It Working?

One of the first issues I encountered was with Xcode.

The full Xcode application was installed, but the active developer directory wasn't pointing to it.

I checked:

xcode-select -p
Enter fullscreen mode Exit fullscreen mode

and got:

/Library/Developer/CommandLineTools
Enter fullscreen mode Exit fullscreen mode

This was an important clue.

Having Apple's Command Line Tools selected doesn't necessarily mean the full Xcode installation is the active developer environment.

I then checked the actual Xcode command-line tooling:

xcodebuild -version
Enter fullscreen mode Exit fullscreen mode

and the simulator tooling:

xcrun simctl list
Enter fullscreen mode Exit fullscreen mode

These checks helped distinguish between:

Xcode is installed
Enter fullscreen mode Exit fullscreen mode

and:

The expected Xcode tooling is available to the development environment
Enter fullscreen mode Exit fullscreen mode

If necessary, the active developer directory can be changed:

sudo xcode-select --switch /Applications/Xcode.app
Enter fullscreen mode Exit fullscreen mode

Then verify again:

xcode-select -p
xcodebuild -version
Enter fullscreen mode Exit fullscreen mode

The lesson

Don't stop at:

"Xcode is installed."

Ask:

"Is the correct Xcode installation active, and can the command-line tooling use it?"

This was one of the first examples that changed how I looked at the setup.


3. When Xcode and MAUI Versions Need to Align

Another important part of the setup was version compatibility.

A .NET MAUI application isn't dependent on a single version.

There are several layers:

.NET SDK
    ↓
.NET MAUI workload
    ↓
.NET iOS workload
    ↓
  Xcode
    ↓
Apple SDK
Enter fullscreen mode Exit fullscreen mode

The project repository can also define specific versions.

For an existing application, files such as:

global.json
Directory.Build.props
*.csproj
Enter fullscreen mode Exit fullscreen mode

can tell you what the project expects.

In my setup, the repository pinned the .NET SDK and MAUI versions. The project used .NET SDK 10.0.203 and MAUI 10.0.60, so simply installing the newest available versions wasn't the goal.

The goal was to reproduce the environment expected by the project.

This is particularly important when setting up an existing application rather than starting a brand-new project.

Capture the versions together

When troubleshooting an iOS build, I found it useful to capture:

dotnet --info
dotnet workload list
xcodebuild -version
Enter fullscreen mode Exit fullscreen mode

Instead of looking at these as independent applications, treat them as one toolchain.

For example:

.NET SDK
   +
MAUI workload
   +
 Xcode
   +
Apple SDK
   =
iOS development toolchain
Enter fullscreen mode Exit fullscreen mode

If one component changes, the others may become relevant.

The lesson

For an existing project, compatibility is more important than simply using the newest version.


4. When Android Setup Doesn't Complete Cleanly

Android setup presented a different challenge.

The initial machine didn't have all the Android development components available.

The setup process needed to establish:

Android SDK
JDK
Build Tools
Platform Tools
ADB
Enter fullscreen mode Exit fullscreen mode

An automated setup process installed Microsoft OpenJDK 21 and bootstrapped the Android SDK, but the overall setup eventually timed out while installing packages.

At that point, it would have been easy to assume:

"The Android setup failed. Start again."

Instead, I checked what had actually been installed.

For example:

java -version
Enter fullscreen mode Exit fullscreen mode

and:

adb version
Enter fullscreen mode Exit fullscreen mode

Then I inspected the Android SDK and its installed components.

This matters because a timeout doesn't necessarily mean that every operation performed before the timeout was unsuccessful.

You may already have:

  • the JDK
  • Android SDK
  • platform tools
  • build tools
  • SDK platforms

installed and ready.

The better approach

Instead of:

Setup failed
    ↓
Reinstall everything
Enter fullscreen mode Exit fullscreen mode

try:

Setup failed / timed out
        ↓
Inspect current state
        ↓
Identify what's missing
        ↓
Install only what's missing
        ↓
Continue verification
Enter fullscreen mode Exit fullscreen mode

The lesson

When an automated setup fails, inspect the resulting environment before starting over.


5. The JDK Exists, But the Build Can't Find It

This was the most interesting Android issue I encountered.

The JDK was installed.

Java was available on the machine.

But the .NET MAUI Android build still failed.

The build reported an XA5300 error because it couldn't locate the Java SDK.

The important part of the investigation was that the problem wasn't simply:

"Java isn't installed."

The JDK existed.

The problem was JDK discovery.

The build process was using Java discovery through:

/usr/libexec/java_home
Enter fullscreen mode Exit fullscreen mode

but it wasn't resolving the installed JDK in the way the build expected.

This highlighted an important distinction:

JDK installed
     ≠
JDK discoverable
     ≠
JDK usable by the build
Enter fullscreen mode Exit fullscreen mode

Step 1: Verify Java

First:

java -version
Enter fullscreen mode Exit fullscreen mode

Then:

/usr/libexec/java_home -V
Enter fullscreen mode Exit fullscreen mode

These commands answer slightly different questions.

The first tells you whether Java is available.

The second helps determine what JDK installations macOS can discover through its Java tooling.

Step 2: Find the actual JDK

The next step was to determine where the JDK was actually installed.

In this setup, the JDK was installed under the Android developer directory.

At this point, the question changed from:

"Is Java installed?"

to:

"What JDK path should the MAUI Android build use?"

Step 3: Explicitly configure the JDK

Rather than continuing to rely on automatic discovery, I configured the build to use the explicit JDK location.

After that change, the Android build progressed successfully.

Why this matters

This pattern applies to many development tools.

A dependency can be:

  • installed
  • visible in the file system
  • available from your terminal

and still be unavailable to the process that actually needs it.

The same idea applies to SDKs, compilers, runtimes, command-line tools, and environment variables.

The lesson

When a build says a dependency is missing, verify whether it is actually missing or simply undiscoverable.

That distinction can save a lot of time.


6. From ADB to a Real Physical Device

Once the Android build environment was working, I wanted to verify the complete development workflow on a physical device.

A physical Android device introduces another layer:

Mac
 ↓
Android SDK
 ↓
ADB
 ↓
Physical device
 ↓
MAUI application
 ↓
Debugger
Enter fullscreen mode Exit fullscreen mode

The first check was deliberately independent of MAUI:

adb devices
Enter fullscreen mode Exit fullscreen mode

The device appeared as an authorized device.

That confirmed:

Mac
 ↓
ADB
 ↓
Device
Enter fullscreen mode Exit fullscreen mode

But that still wasn't enough.

ADB seeing the device doesn't automatically prove that:

  • the MAUI project can build
  • the application can deploy
  • the application can launch
  • the debugger can attach

So I treated these as separate verification steps.

The complete sequence

1. Connect device
        ↓
2. Enable USB debugging
        ↓
3. Authorize the Mac
        ↓
4. Verify with adb devices
        ↓
5. Build the MAUI project
        ↓
6. Deploy the application
        ↓
7. Launch the application
        ↓
8. Attach the debugger
Enter fullscreen mode Exit fullscreen mode

This is much more useful than simply checking whether the device appears in ADB.

The lesson

Verify each boundary separately.

When something fails, you immediately know which layer to investigate.


7. Build Success Isn't the Finish Line

Another lesson from the setup was that a successful build isn't the same as a ready development environment.

It's possible to reach:

Build succeeded
Enter fullscreen mode Exit fullscreen mode

and still have problems with:

  • deployment
  • simulator/device detection
  • application startup
  • debugger attachment
  • runtime behavior

That's why I prefer validating the complete workflow:

Restore
   ↓
 Build
   ↓
 Deploy
   ↓
 Launch
   ↓
 Debug
   ↓
Breakpoint
Enter fullscreen mode Exit fullscreen mode

For iOS, this means verifying that the Simulator can run the application.

For Android, it means going beyond a successful build and validating deployment on an emulator or physical device.

The final test isn't:

"Did the command finish successfully?"

It's:

"Can I actually use this machine for my normal development workflow?"

That is a much stronger definition of "ready."


8. A Systematic Troubleshooting Approach

After going through these issues, I found a simple process useful.

Step 1: Read the error carefully

Don't immediately jump to a fix.

Identify what the error is actually saying.

For example:

Can't find Java SDK
Enter fullscreen mode Exit fullscreen mode

doesn't necessarily mean:

Java isn't installed
Enter fullscreen mode Exit fullscreen mode

It could mean:

The build can't discover the installed Java SDK
Enter fullscreen mode Exit fullscreen mode

Those require different actions.


Step 2: Identify the failing layer

Use the dependency chain:

Project
  ↓
.NET SDK
  ↓
MAUI workload
  ↓
iOS / Android tooling
  ↓
Build tools
  ↓
Simulator / Device
  ↓
Application
Enter fullscreen mode Exit fullscreen mode

Find the smallest layer that explains the failure.


Step 3: Verify the dependency independently

Use simple commands to validate individual components.

For .NET:

dotnet --info
dotnet workload list
Enter fullscreen mode Exit fullscreen mode

For Xcode:

xcode-select -p
xcodebuild -version
Enter fullscreen mode Exit fullscreen mode

For Java:

java -version
/usr/libexec/java_home -V
Enter fullscreen mode Exit fullscreen mode

For Android:

adb version
adb devices
Enter fullscreen mode Exit fullscreen mode

The goal is to separate:

Project problem
Enter fullscreen mode Exit fullscreen mode

from:

Environment problem
Enter fullscreen mode Exit fullscreen mode

Step 4: Compare expected vs actual

This is especially important for existing projects.

Ask two questions:

What does the project expect?
Enter fullscreen mode Exit fullscreen mode

and:

What is the machine actually using?
Enter fullscreen mode Exit fullscreen mode

For example:

Expected SDK version
        vs
Active SDK version
Enter fullscreen mode Exit fullscreen mode

or:

Expected JDK location
        vs
JDK location discovered by the build
Enter fullscreen mode Exit fullscreen mode

Step 5: Change one thing at a time

If you simultaneously change:

  • .NET SDK
  • MAUI workload
  • Xcode
  • JDK
  • Android SDK

and the build starts working, you may never know which change fixed it.

Whenever possible:

Observe
  ↓
Change one thing
  ↓
Verify
  ↓
Continue
Enter fullscreen mode Exit fullscreen mode

This makes troubleshooting much easier to reason about.


Step 6: Re-test the real workflow

After making a change, don't stop at the individual command that previously failed.

Run the actual workflow again:

Restore
   ↓
 Build
   ↓
 Deploy
   ↓
  Run
   ↓
 Debug
Enter fullscreen mode Exit fullscreen mode

That's the final validation.


My Troubleshooting Checklist

.NET

  • [ ] dotnet --info shows the expected SDK
  • [ ] global.json requirements checked
  • [ ] MAUI workload installed
  • [ ] Workload version verified

iOS

  • [ ] Xcode installed
  • [ ] Correct Xcode selected
  • [ ] xcodebuild works
  • [ ] iOS runtimes available
  • [ ] Simulator launches
  • [ ] Project builds for iOS

Android

  • [ ] JDK installed
  • [ ] JDK discoverable by the build
  • [ ] Android SDK installed
  • [ ] Required platform installed
  • [ ] Build tools installed
  • [ ] Platform tools installed
  • [ ] adb available
  • [ ] Device authorized
  • [ ] Project builds for Android

Application

  • [ ] Dependencies restored
  • [ ] Application builds
  • [ ] Application deploys
  • [ ] Application launches
  • [ ] Debugger attaches
  • [ ] Breakpoints work

What I Learned

The biggest change in my approach was moving away from installation-based troubleshooting.

It is easy to think:

"The JDK is installed, so Java isn't the problem."

Or:

"Xcode is installed, so iOS development should work."

But the more useful questions are:

What does the project require?

What is actually installed?

What is currently selected?

Can the build process discover it?

Can the application actually use it?

That gives us a much more useful model:

                PROJECT
                   │
                   ▼
             What does it need?
                   │
                   ▼
             What is installed?
                   │
                   ▼
          What is actually selected?
                   │
                   ▼
        Can the build process find it?
                   │
                   ▼
             Does the build work?
                   │
                   ▼
       Can the application run?
                   │
                   ▼
          Can I actually debug it?
Enter fullscreen mode Exit fullscreen mode

Every failed step reduces the area you need to investigate.


Conclusion

Setting up a development machine isn't really about installing a list of applications.

It's about creating a working chain of dependencies.

When something breaks, the fastest approach isn't always to reinstall everything.

Instead:

Inspect → Isolate → Verify → Fix → Re-test

Start with the actual failure.

Understand which layer is failing.

Verify the dependency independently.

Make the smallest necessary change.

Then test the complete application workflow again.

The most useful distinction I took away from this experience is:

Installed ≠ configured ≠ discoverable ≠ usable.

A tool is only useful when the application and build system can actually find and use it.

That's what turns a development machine from a collection of installed tools into a reliable development environment.

If you haven't read Part 1 yet, it covers the initial setup and verification process that led to these troubleshooting scenarios.

Part 1: Setting Up a Reliable .NET MAUI Development Environment on macOS

Top comments (0)