I just spent two weeks fighting a UE 5.6 bug that has no solution anywhere on the internet. Here’s everything.
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[FIX] UE 5.6 + PICO OpenXR: “xmlns:android is a duplicate attribute name” — full breakdown and working fix
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
I’m building a VR passthrough app for PICO 4 in UE 5.6. At some point I needed to package for Android and got this:
ERROR: AndroidManifest.xml is invalid
System.Xml.XmlException: 'xmlns:android' is a duplicate attribute name.
Simple enough looking error. Took me two weeks to fix. Let me save you that time.
── WHAT’S ACTUALLY HAPPENING ─────────────────────────────────────────────
First thing to understand: this is NOT a PICO plugin bug. I upgraded the SDK, I downgraded the SDK, I tried a completely fresh plugin install. Same crash every time. The bug is in the UE 5.6 engine itself.
Here’s what goes wrong. When UE builds the AndroidManifest.xml, it does two things:
-
It writes the opening tag with all standard namespace declarations — including xmlns:android — from an internal list called ManifestXMLNamespaces.
-
It then loops through ExtraManifestNodeTags (attributes sourced from APL plugin files) and appends them directly to the manifest text string.
The PICO OpenXR APL file has xmlns:android on its tag. In UE 5.6, that attribute gets picked up and written into ExtraManifestNodeTags. So it ends up in the manifest string twice. Then XDocument.Parse() tries to parse that string and immediately explodes, because duplicate XML attributes are invalid.
This didn’t happen in UE 5.5. It’s a 5.6 regression. And since every APL file that does anything with Android namespaces has this pattern, it affects any plugin that follows the standard APL conventions — PICO, and potentially others.
── THE GRAVEYARD OF THINGS I TRIED ──────────────────────────────────────
Before finding the actual fix I went through several rounds of “this should work” → “why doesn’t this work”:
ATTEMPT 1: Remove xmlns:android from PICO’s APL tag.
Logical, right? If the duplicate comes from the APL file, just take it out.
Result: The .NET XML parser threw ‘android’ is an undeclared prefix when trying to load the APL file itself. The declaration needs to be there for the file to be valid XML. Dead end.
ATTEMPT 2: Add inside .
UPL has a removeAttribute command. Perfect.
Result: UPL doesn’t support removing namespace declarations at that processing stage. The duplicate still appeared. Dead end.
ATTEMPT 3: Upgrade to PICO SDK 3.3.0.
Maybe the new version fixed it on their end.
Result: New SDK, same xmlns:android on , same crash. Dead end.
ATTEMPT 4: Fix UEDeployAndroid.cs — remove the duplicate XAttribute from XDoc.Root after ProcessPluginNode.
This is where it got interesting. The fix is definitely in UEDeployAndroid.cs, but I put it in the wrong place. I tried iterating XDoc.Root.Attributes() and removing any duplicate namespace declarations.
Result: Does nothing. .NET’s XDocument doesn’t actually store namespace declarations as regular XAttribute objects — they’re tracked in an internal namespace table. You can iterate them, you can call .Remove() on them, and it will have zero effect on what XDoc.ToString() outputs. The serialized string still has duplicates.
ATTEMPT 5: Post-XDoc.ToString() regex to strip duplicates from the final string.
Getting closer. This would actually work in principle, but the crash happens at XDocument.Parse() — which is called BEFORE ProcessPluginNode, not after. So putting the fix after Parse() is already too late.
Every single attempt failed because I kept misdiagnosing which step produced the duplicate. The duplicate isn’t added by UPL. It isn’t introduced by XDocument serialization. It’s already sitting in the raw text string that gets passed to XDocument.Parse().
── THE ACTUAL FIX ────────────────────────────────────────────────────────
The fix goes at the point where ExtraManifestNodeTags is written to the manifest text — before anything else. We just need to skip any xmlns:prefix= line whose prefix is already declared by the engine.
In UEDeployAndroid.cs, find this block around line 2871:
if (ExtraManifestNodeTags != null)
{
foreach (string Line in ExtraManifestNodeTags)
{
Text.AppendLine(" " + Line);
}
}
Replace it with this:
if (ExtraManifestNodeTags != null)
{
HashSet<string> DeclaredXmlnsPrefixes = new HashSet<string>(
ManifestXMLNamespaces.Select(ns => ns.Alias),
StringComparer.Ordinal);
foreach (string Line in ExtraManifestNodeTags)
{
string Trimmed = Line.Trim();
System.Text.RegularExpressions.Match XmlnsMatch =
System.Text.RegularExpressions.Regex.Match(
Trimmed, @"^xmlns:([a-zA-Z_][\w\-.]*)\s*=");
if (XmlnsMatch.Success &&
DeclaredXmlnsPrefixes.Contains(XmlnsMatch.Groups[1].Value))
{
continue;
}
Text.AppendLine(" " + Line);
}
}
That’s it. Build the engine, done.
The patched file is here if you just want to grab it:
→ GitHub - sdqsz/UnrealEngine5.6AndroidFix: The file for fixing an error · GitHub
── REBUILDING UBT — READ THIS CAREFULLY ─────────────────────────────────
This is the part where I lost another chunk of time. There are TWO copies of UnrealBuildTool.dll on your system. If you only update one of them, the old code keeps running and you’ll think the fix didn’t work.
Step 1. Put the edited UEDeployAndroid.cs here:
C:\Epic Games\unreal\UE_5.6\Engine\Source\Programs\UnrealBuildTool\Platform\Android\UEDeployAndroid.cs
Step 2. Build UBT. Run these as two separate commands (don’t chain them):
cd "C:\Epic Games\unreal\UE_5.6\Engine\Source\Programs\UnrealBuildTool"
"C:\Epic Games\unreal\UE_5.6\Engine\Binaries\ThirdParty\DotNet\8.0.300\win-x64\dotnet.exe" build UnrealBuildTool.csproj -c Development
You should see “Build succeeded” with 0 errors. Two warnings about a Microsoft.Build vulnerability are normal, ignore them.
Step 3. Copy the compiled DLL to BOTH locations:
copy "C:\Epic Games\unreal\UE_5.6\Engine\Source\Programs\UnrealBuildTool\bin\Development\UnrealBuildTool.dll" "C:\Epic Games\unreal\UE_5.6\Engine\Binaries\DotNET\UnrealBuildTool\UnrealBuildTool.dll"
copy "C:\Epic Games\unreal\UE_5.6\Engine\Binaries\DotNET\UnrealBuildTool\UnrealBuildTool.dll" "C:\Epic Games\unreal\UE_5.6\Engine\Binaries\DotNET\AutomationTool\UnrealBuildTool.dll"
The second path — DotNET\AutomationTool\ — is the one UAT actually loads when you hit Package Project from the editor. It’s not obvious that this copy exists separately. If you skip it, you’ll rebuild, copy, repackage, see the exact same crash, and wonder what went wrong. Type “yes” when prompted to overwrite.
Step 4. Repackage from the editor. That’s it.
── WHO THIS AFFECTS ──────────────────────────────────────────────────────
✓ UE 5.6 + PICO OpenXR plugin (any version)
✓ UE 5.6 + any other Android plugin whose APL file has xmlns:android, xmlns:dist, or xmlns:tools on the tag
✗ UE 5.5 and earlier — this is a 5.6-specific regression
If you’re hitting this crash with a different plugin than PICO, the fix is identical. The root cause is the same.
Good luck.