Wanna see something very weird in AL?

A couple of days ago, we had a strange error preventing us from deploying an app that we have deployed 100s of times before. Check out the video:

https://youtu.be/5MbRACn7bnk

In this video, Erik demonstrates a strange and subtle bug that can occur in AL for Business Central — one that compiles successfully but completely fails during deployment. The issue stems from legacy code that used option values which no longer exist in the current context, and it reveals an interesting quirk in how the AL compiler handles (or mishandles) unresolved option references.

The Problem: Code That Compiles But Won’t Deploy

Erik encountered this issue in production code that had survived since around 2007-2008, migrated all the way through to modern AL extensions. The code compiled without errors — only warnings — but when attempting to deploy, it failed with a cryptic error:

The request failed with code 422: Failure while emitting method 
"OnOpenPage" - Object reference not set to an instance of an object.

That last part — “Object reference not set to an instance of an object” — is essentially the .NET equivalent of a null pointer exception. Something that should have been resolved to a value was, in fact, nothing at all.

Understanding the Two-Phase Compilation

To understand why this happens, you need to know that AL goes through two compilation phases:

  1. Client-side compilation: When you build your app locally, the compiler generates an .app file containing mostly source code along with symbols and extracted metadata. This is the step that succeeds.
  2. Server-side transpilation: When you upload and deploy the app, the server performs another compilation pass — but this time it’s actually generating C# code. It’s a transpiler, not just a compiler. This is the step that breaks.

So the code passed the first gate but crashed at the second. From one Business Central version to the next, something changed at Microsoft that caused previously tolerated code to fail during the server-side transpilation.

The Root Cause: Phantom Option Values

Looking at the warnings the compiler did produce, the key one was:

The name 'PostingMethod' does not exist in the current context. 
This warning will become an error when targeting a runtime version 12 or higher.

The code in question called a function that accepted option parameters, passing in what appeared to be valid option values — but those option member names didn’t actually exist in any defined option type in the current context. Here’s a simplified example of what was happening:

trigger OnOpenPage()
begin
    TestIsAnOption("Sales Line Type"::Item);
    // 'PostingMethod' doesn't exist anywhere — but this compiles!
    SomeFunction(PostingMethod::OpenEntry);
end;

The bizarre behavior is that the AL compiler accepts any value formatted as an option member (i.e., SomeName::SomeValue), even when SomeName doesn’t exist in the current context. You could write RandomSomething::AnyValue and it would compile — you’d just get a warning, not an error.

Why It Worked Before (By Pure Dumb Luck)

Erik suspects that when the compiler can’t resolve an option value, it silently resolves to zero — the first option value. In their legacy code, the option they actually needed happened to be the first one (option zero), so the code behaved correctly by sheer coincidence. The logic worked exactly as intended, but only by accident.

It’s possible that at some earlier point in the code’s history, the option type did exist in context, but after years of refactoring and migration, it no longer did — and nobody noticed because the behavior didn’t change.

The Warning and Its Documentation

The compiler warning (AL0731) includes a link to Microsoft’s documentation, but Erik notes that the page is essentially empty — just restating the warning message and noting that it will become a hard error with 2023 release wave 2 (runtime version 12). Not very helpful for understanding what’s actually going on.

Key Takeaways

  • Don’t ignore compiler warnings. This is the textbook case for why warnings matter. What was “just a warning” on the client side caused a hard deployment failure on the server.
  • The AL compiler is surprisingly permissive with option values. It will accept option member syntax even when the option type doesn’t exist, resolving it silently (likely to zero).
  • Legacy code migrations require careful review. Code that has survived from C/AL through to AL may carry assumptions that are no longer valid.
  • The client-side compiler and server-side transpiler have different tolerance levels. Code that passes local compilation can still fail during deployment — a frustrating but important distinction to be aware of.
  • This warning will become a hard error. Starting with runtime version 12 (2023 release wave 2), unresolved option names will no longer compile at all, so it’s important to clean up this kind of code now.

Conclusion

This is one of those subtle, maddening bugs that can lurk in a codebase for years without anyone noticing. The AL compiler’s willingness to accept undefined option values as valid syntax — demoting what should be an error to a mere warning — allowed broken code to slip through. When the server-side transpiler tightened its validation, the code that had “always worked” suddenly couldn’t deploy at all. The lesson is clear: treat warnings as errors, especially in legacy code that has been migrated across multiple platform generations. And if you’re still running code from 2007 — give it a thorough review.