Why My AL MCP Server Could Not Publish or Run Tests

Introduction

Most of my day-to-day work is building AL extensions for Microsoft Dynamics 365 Business Central, and for the last while I have been doing a growing part of that work with an AI coding agent sitting next to me in the editor. That only pays off if the agent can actually do things rather than just talk about them, and in practice that means MCP servers.

MCP (Model Context Protocol) is the standard way an agent gets access to real tools. Instead of the agent guessing whether my code compiles, it calls a tool that compiles it and reads the diagnostics back. Instead of me pasting error messages into a chat window, the tool returns them directly. The difference between an agent that can only suggest code and one that can compile, publish and test it is roughly the difference between a rubber duck and a colleague.

For AL development, the relevant one is the AL MCP server that ships inside the AL Language extension for Business Central. It exposes the operations that make up my normal inner loop:

  • al_compile and al_build — validate the code, generate the .app package
  • al_getdiagnostics — read errors and warnings back without recompiling
  • al_downloadsymbols and al_getpackagedependencies — manage the symbol cache and inspect app.json dependencies
  • al_symbolsearch — find tables, codeunits, pages, fields across the project and its dependencies
  • al_publish — push the extension to a Business Central server or container
  • al_run_tests — run a test codeunit and get pass/fail results

That last pair is where the real value is. Compiling is nice, but a change to a billing calculation is not verified until the tests have actually run against a server. If al_publish and al_run_tests work, the agent can close the loop on its own: build, deploy, test, read the failures, fix, repeat. If they do not, I am back to alt-tabbing into the IDE for every iteration, and most of the benefit evaporates.

On my machine, those two were exactly the ones that did not work.

Issue

The symptom was oddly specific: everything local worked, and everything that touched the server failed.

These were all fine:

al_getpackagedependencies  ->  OK, full dependency list returned
al_symbolsearch            ->  OK, symbols found across dependencies
al_compile                 ->  OK, diagnostics returned
al_build                   ->  OK, .app package generated

And then, calling al_run_tests against my on-premise development server with user name and password authentication:

{
  "succeeded": false,
  "message": "Exception of type 'Microsoft.Dynamics.Nav.Deployment.Authentication.UserNotAuthenticatedException' was thrown.",
  "errorDetails": {
    "code": "UnknownError",
    "description": "An unknown error occurred",
    "possibleCauses": [
      "Extension internal error",
      "Unexpected system state"
    ],
    "suggestedActions": [
      "Try the operation again",
      "Restart VS Code",
      "Check AL extension output for details"
    ]
  }
}

al_publish failed the same way. The buried UserNotAuthenticatedException is the only useful part of that payload; everything around it is generic filler. “Extension internal error”, “Unexpected system state” and “Restart VS Code” point at three things that were not the problem, which is worse than saying nothing at all.

What made it genuinely confusing is that authentication was never prompted for and never failed anywhere else. Publishing the same extension to the same server from the IDE with F5 worked perfectly, and had worked for months. So the credentials clearly existed and were clearly valid. The MCP server just could not see them.

Reason

The cause turned out to be that I had two different builds of the same tool installed, and the MCP server was launched with the wrong one.

The AL MCP server is started by running altool launchmcpserver with a list of project folders. On a typical development machine, altool exists in two places:

  1. The dotnet global tool. Installing the Microsoft.Dynamics.BusinessCentral.Development.Tools NuGet package puts al.exe on the PATH, at something like C:\Users\dev\.dotnet\tools\al.exe.
  2. The binary inside the AL Language extension, at C:\Users\dev\.vscode\extensions\ms-dynamics-smb.al-17.0.1234567\bin\win32\altool.exe.

Both accept launchmcpserver. Both report the same version number. Both start a working MCP server that answers every tool call. My configuration used the convenient one:

"al-mcp": {
  "type": "stdio",
  "command": "al",
  "args": ["launchmcpserver", "C:/git_repos/MyProduct/MyApp", "..."]
}

The difference between the two only shows up when a tool needs on-premise credentials. Business Central’s UserPassword authentication for a development server is cached by the AL extension inside its own installation folder:

C:\Users\dev\.vscode\extensions\ms-dynamics-smb.al-17.0.1234567\bin\win32\UserPasswordCache.dat

Not in Windows Credential Manager. Not in the IDE’s secret storage. In a file sitting next to altool.exe, in a folder whose name contains the extension version. I confirmed this by checking timestamps: the cache file predated my session by weeks — it was created by an ordinary manual publish from the IDE — and its last-access time updated to the exact moment a working MCP call ran. The MCP server does not create that cache; it reads a cache the extension created earlier.

Which explains everything. The dotnet global tool lives in a completely different directory tree and has no such file anywhere in its payload. So it starts a perfectly healthy MCP server that can parse, compile and build all day long — and the moment a tool needs to authenticate against a server, it has no credentials, cannot prompt for any (there is no interactive console in an MCP call), and throws UserNotAuthenticatedException.

Two things I tried along the way that are worth warning about:

Environment variables do not help. My original configuration carried this, presumably from an earlier attempt at exactly this problem:

"env": {
  "BC_SERVER_USERNAME": "admin",
  "BC_SERVER_PASSWORD": "..."
}

altool has no such mechanism. Those variables did nothing at all except keep a plaintext password in a configuration file, which is a small security problem masquerading as a fix. I removed them.

The obvious fix has its own trap. Pointing command straight at the extension’s altool.exe works, but look at that path again — it contains 17.0.1234567. Every time the AL extension updates, it installs into a new versioned folder and the old one goes away. The configured path stops existing, the MCP server silently fails to launch, and there is no error until you try to use a tool. Worse, the credential cache lives in that same doomed folder, so an extension update loses the cached credentials too.

So the two candidates each solve half the problem: the dotnet tool has a stable path but no credentials, and the extension binary has the credentials but an unstable path.

Solution

The fix is a small launcher script that resolves the newest installed extension at launch time, so the configuration can point at a path that never changes while still running the binary that owns the credential cache. It also copies the cache forward when it finds a fresh extension folder without one.

@echo off
rem Stable launcher for the AL extension's altool.exe.
rem Diagnostics go to stderr only - stdout is the MCP pipe.
setlocal enabledelayedexpansion

set "EXTROOT=%USERPROFILE%\.vscode\extensions"
set "NEWDIR="
set "OLDCACHE="

rem Newest extension folder first; first one with a usable altool.exe wins.
for /f "delims=" %%D in ('dir /b /ad /o-d "%EXTROOT%\ms-dynamics-smb.al-*" 2^>nul') do (
    if exist "%EXTROOT%\%%D\bin\win32\altool.exe" (
        if not defined NEWDIR set "NEWDIR=%EXTROOT%\%%D\bin\win32"
    )
)

if not defined NEWDIR (
    echo [al-mcp shim] No altool.exe found under "%EXTROOT%\ms-dynamics-smb.al-*". 1>&2
    exit /b 1
)

rem An extension update creates a folder without the credential cache.
rem Carry the newest existing one forward so on-prem auth survives updates.
if not exist "%NEWDIR%\UserPasswordCache.dat" (
    for /f "delims=" %%D in ('dir /b /ad /o-d "%EXTROOT%\ms-dynamics-smb.al-*" 2^>nul') do (
        if exist "%EXTROOT%\%%D\bin\win32\UserPasswordCache.dat" (
            if not defined OLDCACHE set "OLDCACHE=%EXTROOT%\%%D\bin\win32\UserPasswordCache.dat"
        )
    )
    if defined OLDCACHE copy /y "!OLDCACHE!" "%NEWDIR%\UserPasswordCache.dat" >nul 2>&1
)

"%NEWDIR%\altool.exe" %*
exit /b %ERRORLEVEL%

Two details in there matter more than they look:

Keep the script’s own output on stderr. Its stdout is the JSON-RPC pipe between the agent and the MCP server. One stray echo on stdout corrupts the protocol stream.

Sort by write time, not by name. Version folders sorted as strings will eventually order wrongly; newest-modified-first is what you actually want.

The configuration then invokes the script through cmd.exe:

"al-mcp": {
  "type": "stdio",
  "command": "cmd.exe",
  "args": [
    "/c", "C:\\Users\\dev\\bin\\al-mcp.cmd",
    "launchmcpserver",
    "C:/git_repos/MyProduct/MyApp",
    "C:/git_repos/MyProduct/MyAppTest",
    "--transport", "stdio",
    "--packagecachepath", "C:/git_repos/MyProduct/.alpackages"
  ],
  "env": {}
}

The cmd.exe /c wrapper is deliberate. Node cannot spawn a .cmd file without a shell, and cmd.exe is a dumb, byte-faithful pass-through for stdio — unlike a PowerShell wrapper, which can helpfully mangle encoding and line endings on a JSON-RPC stream.

After restarting the editor, I confirmed the running process was the right binary:

Get-CimInstance Win32_Process -Filter "Name='altool.exe'" |
    Select-Object -ExpandProperty ExecutablePath

C:\Users\dev\.vscode\extensions\ms-dynamics-smb.al-17.0.1234567\bin\win32\altool.exe

And then the call that had been failing:

al_run_tests  codeunitId=50100
              serverUrl=http://bc-dev  serverInstance=BC
              authentication=UserPassword  tenant=default

Using user name and password authentication. User name used is: 'admin'.
Sending request to http://bc-dev:7049/BC/dev/metadata?tenant=default
Test hub connected.
Test run completed: 2 passed, 0 failed, 0 skipped.

No prompt, no credentials in the call, no plaintext password in any configuration file. The cached credential that the IDE created weeks earlier was simply read by the binary that owns it. With that working, a full run went through end to end — build the dependency chain, publish to the development server, and execute the whole test suite: 36 test codeunits, 819 passing.

Two smaller things came out of the same exercise and are worth knowing if you set this up yourself. First, the AL MCP server does not read the global launch configuration from user settings, only a workspace-level launch.json; with no launch.json in the repository it logs No AL launch configuration found in launch.json and falls back to whatever the tool call passed, so I now pass serverUrl, serverInstance, authentication and tenant explicitly every time. Second, if the extension ever updates and the carried-forward credential cache does not decrypt, the recovery is one manual publish from the IDE to reseed it — an MCP call cannot answer an interactive credential prompt, so that step stays manual by design.

The broader lesson I am taking from this: when a set of tools splits cleanly into “works” and “does not work” along the line of needs credentials, stop reading the error message and start asking which binary is actually running. Two identical version numbers on two different paths cost me more time than the fix did.

“UnprocessableEntity” in Business Central App Publishing: Specified part does not exist in the package

If you’re publishing AL apps from VS Code to a Business Central Docker container and hitting this error:

[2026-06-19 11:07:27.48] The request for path /BC/dev/apps?tenant=default&SchemaUpdateMode=forcesync&ForceUpgrade=true&DependencyPublishingOption=default failed with code UnprocessableEntity. Reason: Publishing failed due to 'Specified part does not exist in the package'. The original extensions have been restored.

You’ve already verified: compilation succeeded, no dependency issues in the build output, warnings are minimal. Yet publishing fails silently. This is maddening because the error message gives you nothing to work with.

Initial Troubleshooting (The Wrong Path)

My first instinct was to blame Business Central container versioning. I checked app dependencies, reviewed event logs in the container, verified app.json configurations. Everything looked correct. The apps compiled cleanly in VS Code without a single error or warning. Dependencies resolved properly. Yet the runtime rejected them during publishing.

Hours of digging through Docker event logs later, I finally checked something obvious: the AL Language extension version running in VS Code.

The Real Issue: Compiler Adding Packages Not Supported by Runtime

Here’s what was actually happening:

I was running AL Language extension v18.0.x (prerelease) while my Docker container was Business Central 28.1 running runtime 17.0.x. The newer AL compiler in the v18.0.x extension includes features and compiler optimizations that pack additional metadata or package structures into the compiled .app files. These new internal packages don’t exist in runtime 17.0.x.

When the BC runtime tried to deserialize and validate the compiled app package, it couldn’t find these new internal components. The “Specified part does not exist in the package” error is the runtime’s way of saying: “I found references to package components that aren’t in my schema.”

The apps compiled successfully because VS Code’s AL compiler just packaged everything according to its current version. But when the older runtime tried to unpack and process those same apps, the version mismatch caused deserialization to fail.

The Fix

Downgrade your AL Language extension to match the runtime version in your Docker container. Check your container’s BC version, identify the corresponding runtime version, then pin your AL extension to a compatible version. Recompile and republish.

In my case: AL extension downgraded to v17.x to match runtime 17.0.x on BC 28.1. Problem solved immediately.

End Note: Why This Isn’t Obvious

Business Central’s versioning scheme doesn’t help here. BC version 28 is marketed as “2026 Release Wave 1” but runs on runtime 17.0.x. There’s no intuitive numerical relationship. Without explicitly checking your container’s runtime version, you won’t know which extension version to target. Microsoft could improve this by making version relationships explicit in documentation or extension metadata.

TL;DR: Newer AL compiler versions pack features unsupported by older runtimes. Match your extension version to your runtime version, recompile, and republish.

Visual Studio – no .NET SDKs were found

When trying to change a VS project to implement some new Azure function need by our Business Central implementation of certain feature Visual Studio was unable to detect dotnet SDKs although multiple versions were installed.

So running

dotnet --info

has given me the following output:

So I have tried running Visual Studio repair multiple times. Installed dotnet SDK for that exact version of Visual Studio 2020 CE. Checking PATH environment variables multiple times. Restarting machine multiple times to no avail.

Solution

However, the solution was right into my nose but… failing to see the obvious. I have suspected that it has something to do with both x86 and x64 versions of .NET installed but could not figured it out what is the difference.

Solution was quite easy. Running

where.exe dotnet

will give you the result of order in which paths of dotnet search is executed.

 If you’re on a x64 machine, you want to have C:\Program Files\dotnet listed first.

Now this is the output of the dotnet –info command:

NAV 2013 R2 RunPageLink bug

Bug

If you have standard Page Action that uses RunPageLink on field that is an Option field, and the OptionCaption of that field has entry that uses & sign (“and” sign) the RunPageLink will not filter the underlying table correctly in Filter Group0.

Explanation

The standard example would be Comment Line Table. If you define Page Action to run Comment Line Table, from some page, where RunPageLink uses filter on the field, that is of Option type and the OptionCaption (OptionCaptionML) of the field has & sign (“and” sign):

2014-09-01_1522

then the standard filter will not work:

2014-09-01_1507

After you change the OptionCaption (OptionCaptionML) of the field that is in RunPageLink filter to (note that OptionString still has & sign):2014-09-01_1510

the standard filter works as it is supposed to:

2014-09-01_1513Of course, when you run Page Action any other value of Option field, that doesn’t have & sign in OptionCaption, RunPageLink works properly.

 

Add multiple Excel worksheets to single Excel workbook – Dynamics NAV 2013

In Microsoft Dynamics NAV 2013 Excell Buffer table (370) changed from using Excel Automation variable to Open XML 2.0 DotNet interoperability classes. More information about that can be found on NAV Team blog in article Excel Buffer Using Open XML Instead of Excel Automation.

The one function that is needed, in real life, when exporting data to Excel workbook is to export data to the same Excel workbook but to different worksheets.

Let say you want to export Item and Item Ledger Entry table to single Excel workbook. I haven’t found any function in ExcelBuffer that does that – correct me if I am wrong.

So I decided to write function that enables just that, open single Excel workbook and just adds new worksheet one after another. In Excel Buffer table (370) I have created AddNewSheet function that receives only one parameter (SheetName).

AddNewSheet(SheetName : Text[250])
IF SheetName = '' THEN
  ERROR(Text002);

IF ISNULL(XlWrkBkWriter) THEN BEGIN
  CreateBook(SheetName);
  ActiveSheetName := SheetName; //first sheet activated
END ELSE BEGIN
  WHILE XlWrkBkWriter.HasWorksheet(SheetName) DO BEGIN
    IF NOT FirstIncrement THEN BEGIN
      SheetName := SheetName + '-01';
      FirstIncrement := TRUE;
    END ELSE BEGIN
      SheetName := INCSTR(SheetName);
    END;
  END;
  XlWrkShtWriter := XlWrkBkWriter.AddWorksheet(SheetName);
  FirstIncrement := FALSE;
  //ActiveSheetName := SheetName; //last sheet activated
END;
WriteSheet('',COMPANYNAME,USERID);
DELETEALL;
ClearNewRow;

Additional variable that I have used is FirstIncrement (Boolean) – all other are global variables in Excel Buffer table.

Let me explain few key points in this code. First in line 5 we check if the WorkSheetWriter is initialized. If it is not initialized we call standard CreateBook function and set the ActiveSheetName to that “first” excel sheet.

In line 9 we check if there is sheet in workbook that has the same name as the one we have passed as parameter – which can happen in subsequent calls. If this is true then we append ‘-01’ so we can use INCSTR(SheetName) until we find unused SheetName – so we don’t overwrite data on existing sheet.

Then the main task of this function is executed  in line 17 when we append new worksheet to currently open workbook.

In line 19 I have commented out line of code that sets the last sheet that we call to be initially shown when we open Excel workbook. I really want the first worksheet to be shown but you might want it the other way.

Finally, we WriteSheet and delete all data currently in Excel Buffer.

When the code, that populates data in Excel Buffer for single sheet, is finished we have to call this function to write sheet and flush all the data so it is free for another sheet.

Also, there is a difference that we cannot use standard function CreateBookAndOpenExcel – because it is used for single worksheet case scenario – but we must “close” Excel manually by calling standard functions CloseBook, OpenExcel, GiveUserControl on Excel Buffer.

Test codeunit that simulates usage of this function follows.

OBJECT Codeunit 50000 Test AddNewSheet
{
  OBJECT-PROPERTIES
  {
    Date=27.08.14;
    Time=10:58:00;
    Modified=Yes;
    Version List=;
  }
  PROPERTIES
  {
    OnRun=BEGIN
            CreateItemSheet;
            CreateItemLedgerEntrySheet;
            CreateItemSheet;
            CreateItemSheet;
            CreateItemLedgerEntrySheet;
            CreateItemLedgerEntrySheet;
            CreateItemSheet;
            CreateItemSheet;
            CreateItemLedgerEntrySheet;
            CreateItemLedgerEntrySheet;

            ExcelBuff.CloseBook;
            ExcelBuff.OpenExcel;
            ExcelBuff.GiveUserControl;
          END;

  }
  CODE
  {
    VAR
      Item@1002 : Record 27;
      ItemLedgerEntry@1001 : Record 32;
      ExcelBuff@1000 : TEMPORARY Record 370;

    PROCEDURE CreateItemSheet@1();
    BEGIN
      ExcelBuff.AddColumn(FORMAT(Item.FIELDCAPTION("No.")),FALSE,'',TRUE,FALSE,FALSE,'',ExcelBuff."Cell Type"::Text);
      ExcelBuff.AddColumn(FORMAT(Item.FIELDCAPTION(Description)),FALSE,'',TRUE,FALSE,FALSE,'',ExcelBuff."Cell Type"::Text);
      ExcelBuff.AddColumn(FORMAT(Item.FIELDCAPTION("Base Unit of Measure")),FALSE,'',TRUE,FALSE,FALSE,'',ExcelBuff."Cell Type"::Text);
      ExcelBuff.AddColumn(FORMAT(Item.FIELDCAPTION(Inventory)),FALSE,'',TRUE,FALSE,FALSE,'',ExcelBuff."Cell Type"::Text);
      ExcelBuff.NewRow;

      Item.RESET;
      IF Item.FINDSET THEN REPEAT
        Item.CALCFIELDS(Inventory);
        ExcelBuff.AddColumn(FORMAT(Item."No."),FALSE,'',FALSE,FALSE,FALSE,'',ExcelBuff."Cell Type"::Text);
        ExcelBuff.AddColumn(FORMAT(Item.Description),FALSE,'',FALSE,FALSE,FALSE,'',ExcelBuff."Cell Type"::Text);
        ExcelBuff.AddColumn(FORMAT(Item."Base Unit of Measure"),FALSE,'',FALSE,FALSE,FALSE,'',ExcelBuff."Cell Type"::Text);
        ExcelBuff.AddColumn(FORMAT(Item.Inventory),FALSE,'',FALSE,FALSE,FALSE,'',ExcelBuff."Cell Type"::Text);
        ExcelBuff.NewRow;
      UNTIL Item.NEXT = 0;

      ExcelBuff.AddNewSheet(Item.TABLECAPTION);
    END;

    PROCEDURE CreateItemLedgerEntrySheet@2();
    BEGIN
      ExcelBuff.AddColumn(FORMAT(ItemLedgerEntry.FIELDCAPTION("Item No.")),FALSE,'',TRUE,FALSE,FALSE,'',ExcelBuff."Cell Type"::Text);
      ExcelBuff.AddColumn(FORMAT(ItemLedgerEntry.FIELDCAPTION("Posting Date")),FALSE,'',TRUE,FALSE,FALSE,'',ExcelBuff."Cell Type"::Text);
      ExcelBuff.AddColumn(FORMAT(ItemLedgerEntry.FIELDCAPTION("Document No.")),FALSE,'',TRUE,FALSE,FALSE,'',ExcelBuff."Cell Type"::Text);
      ExcelBuff.AddColumn(FORMAT(ItemLedgerEntry.FIELDCAPTION(Quantity)),FALSE,'',TRUE,FALSE,FALSE,'',ExcelBuff."Cell Type"::Text);
      ExcelBuff.AddColumn(FORMAT(ItemLedgerEntry.FIELDCAPTION("Remaining Quantity")),FALSE,'',TRUE,FALSE,FALSE,'',ExcelBuff."Cell Type"::Text);
      ExcelBuff.AddColumn(FORMAT(ItemLedgerEntry.FIELDCAPTION("Entry No.")),FALSE,'',TRUE,FALSE,FALSE,'',ExcelBuff."Cell Type"::Text);
      ExcelBuff.NewRow;

      ItemLedgerEntry.RESET;
      ItemLedgerEntry.SETCURRENTKEY("Item No.","Posting Date");
      IF ItemLedgerEntry.FINDSET THEN REPEAT
        ExcelBuff.AddColumn(FORMAT(ItemLedgerEntry."Item No."),FALSE,'',FALSE,FALSE,FALSE,'',ExcelBuff."Cell Type"::Text);
        ExcelBuff.AddColumn(FORMAT(ItemLedgerEntry."Posting Date"),FALSE,'',FALSE,FALSE,FALSE,'',ExcelBuff."Cell Type"::Date);
        ExcelBuff.AddColumn(FORMAT(ItemLedgerEntry."Document No."),FALSE,'',FALSE,FALSE,FALSE,'',ExcelBuff."Cell Type"::Text);
        ExcelBuff.AddColumn(FORMAT(ItemLedgerEntry.Quantity),FALSE,'',FALSE,FALSE,FALSE,'',ExcelBuff."Cell Type"::Number);
        ExcelBuff.AddColumn(FORMAT(ItemLedgerEntry."Remaining Quantity"),FALSE,'',FALSE,FALSE,FALSE,'',ExcelBuff."Cell Type"::Number);
        ExcelBuff.AddColumn(FORMAT(ItemLedgerEntry."Entry No."),FALSE,'',FALSE,FALSE,FALSE,'',ExcelBuff."Cell Type"::Number);
        ExcelBuff.NewRow;
      UNTIL ItemLedgerEntry.NEXT = 0;

      ExcelBuff.AddNewSheet(ItemLedgerEntry.TABLECAPTION);
    END;

    BEGIN
    {
      ZORAN - Excel buffer AddNewSheet Test
    }
    END.
  }
}

I hope this helps someone and please feel free to comment.

 

Dynamics NAV “The Object table does not exist.” error

The Problem

One week ago we started experiencing problem with compiling and importing objects into our clients test database. The error which pop-ups is the “The Object table does not exist.”. Aaaah… we had our share of headaches with NAV 2013 R2 architecture and we are sceptic about everything in nowadays.

The problem was even more curious because the same database worked well in our development environment. When we restore the SQL backup on our client’s test environment we cannot import or compile objects.

The Resolution

What I have determined, after SQL tracing, is that we had transfered trigger, in Object table of our development NAV database, along with the SQL backup :).

The trigger is used for SourceSafe database, for Object table on our development databases, so we could roll-back objects that had been changed or deleted and we had to restore previous versions.

The misleading of the displayed error was in the unfortunate coincidece of the name of “Object” tables both in our SourceSafe and NAV databases.

Dynamics NAV NAS server error “Function sequence error” & “Invalid cursor state”

The problem

While trying to setup NAS server for client to do the nightly Job Queue events we were stuck with the problem of NAS failing to do anything after starting up.

In the event log there were three Event ID 20010 Warnings:

The following ODBC error occurred:
Error: [Microsoft][ODBC Driver Manager] Function sequence error
State ID: HY010
The following ODBC error occurred:
Error: [Microsoft][ODBC SQL Server Driver]Invalid cursor state
State ID: 24000
The Application Server for Microsoft Dynamics NAV NAVSRVAPL-SQL could not initialize properly.
The server will attempt to initialize every 30 seconds until this
is successful.

When starting without any jobs in the Ready state NAS would initialize but didn’t have these warnings because it had nothing to do.

The Resolution

After series of investigations we came up with the clue to investigate further. I created the test table that had only two fields: 1. Primary key, 2. Datetime. As the primary key field was setup as Autoincrement we managed to recreate the error we saw with NAS using the same user that was used to start NAS. This was strange because this user had dbo role in SQL server that is required for user when using Autoincrement property – as NAV Development Documentation states. When removing Autoincrement property from the primary key field the error didn’t appear anymore.

We setup simple codeunit, that would increment this simple table, in Job Queue but the problem reappeared again. After “digging deep” in the NAS initialization process in standard NAV codeunits we found out that the two tables that are used in this process also had Autoincrement property set to YES. These are tables 405 Change Log Entry and 474 Job Queue Log Entry.

Because inserting records in these two tables were failing the NAS failed to do anything but to report error. We removed Autoincrement property and added code to populate primary keys of these two tables incrementaly. After that the NAS started normally and was executing jobs in Job Queue.

The Conclusion

We believe that the problem is, still, with the assigned SQL rights for the user that is running NAS, although it had db_owner rights on that database, but we could not investigate further because this was production environment. All the other NAS implemenations were working properly without this “intervention” on the standard Microsoft Dynamics NAV code or tables. But this is the workaround we had to implement to overcome this situation.

Update

After moving client’s database to another SQL server this problem went away (as did the few other). This encourages me to believe that the db_owner rights were messed up somehow on initial SQL server.

Dynamics NAV 2013 RapidStart package import error

RapidStart Services are introduced in Dynamics NAV 2013 as a mean of quickly setting up a new company with predefined data. It has great benefits for NAV implementers when working with new clients or setting up new company in test environment.

While working on localization of NAV 2013 for Serbia we started having problems with importing Rapidstart package files. The error we got, while testing, was “The specified file could not be imported because it is not a valid RapidStart package file.

2013-09-30_1128
Error window

We have found out that the problem was in Codeunit 8619 Config. Pckg. Compression Mgt. The function IsGZip did not return true in our case, although the file was created with the same database for NAV 2013.

The source of the problem was that the Windows Server, on which our NAV Server was installed, had Regional Settings for Language for non-Unicode programs set to “Serbian (Latin, Serbia and Montenegro (Former))”.

So we had two options to resolve the error.

First solution

This was simple as setting the Regional Settings for Language for non-Unicode programs set back to English (United States). After restarting the Windows Server the error was gone. I would recommend this solution wherever this setting does not interfere with other software installed on NAV Server.

Second solution

This solution is “not the best option” but it is focused on changing the IsGZip function in Codeunit 8619 Config. Pckg. Compression Mgt. so it returns TRUE after uploading file to NAV Server. I would not recommend this solution but if there are no other options then…

Device installation. The system cannot find the file specified.

While updating Windows XP on one of my computers I couldn’t get any of the hardware drivers to install or update properly. All tries have ended with error:

An error occurred during the installation of the device.
The system cannot find the file specified.

The solution was to go to HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion
and check if the RunOnce registry key exists. For some reason this key was missing – it was renamed to RunOnceEx. After recreating key RunOnce in the above registry tree all the driver installations afterwards were successfull.

 

Building and using MS Project 2010 proxy assembly for WCF PSI Service

To do the task of integrating Microsoft Dynamics NAV 2009 R2 and MS Project Server 2010 I have decided to use WCF interface. WCF interface is provided by PSI (Project Server Interface) which has both ASMX object model and WCF object model implemented.

There are three options for communicating with WCF interface of MS Project Server:

  1. Compiling ProjectServerServices.dll PSI proxy assembly.
  2. Add a PSI proxy source code to the Visual Studio solution.
  3. Add a service reference by using Visual Studio.

I have decided to use the first option and compile ProjectServerServices.dll proxy assembly.

Project 2010 SDK

First you need to download and install Project 2010 SDK from Microsoft Download site (Project 2010 Reference: Software Development Kit).

After you have installed Project 2010 SDK you need to go to the installation folder. In my case that was folder: C:\Program Files (x86)\Microsoft SDKs\Project 2010\Documentation\Intellisense\WCF. Unpack the Source.zip file, found in that folder, so you get the Source subfolder containing C# source files.

Next, you need to start CompileWCFProxyAssembly.cmd to create ProjectServerServices.dll file. The best way to do it is to open Command prompt with administrative privileges (Start->All Programs->Accessories->Command prompt then right click and select Run as administrator), then cd to the C:\Program Files (x86)\Microsoft SDKs\Project 2010\Documentation\Intellisense\WCF and then run CompileWCFProxyAssembly.cmd.

Note: You need to change the path of sn (sn.exe) to the location of Windows SDK in CompileWCFProxyAssembly.cmd. In my case that was C:\Program Files (x86)\Microsoft SDKs\Windows\v8.0A\bin\NETFX 4.0 Tools\x64\sn.exe.

After the script has run you should have ProjectServerServices.dll in this folder.

Visual Studio

In Visual Studio you shoud add reference by clicking right mouse button on Reference folder in your C# project then select Add reference… option, then select Browse on the left hand side of the Reference Manager window and click Browse… button on the lower side of the window. Navigate to C:\Program Files (x86)\Microsoft SDKs\Project 2010\Documentation\Intellisense\WCF\ProjectServerServices.dll file and click Add button. Click OK to close the Reference Manager window.

Now have fun with connecting to the MS Project Server PSI using WCF interface.