On this page:
8.1 Enabling logging
8.2 Log levels
8.3 Request logging
8.4 Stopping the listener
8.5 Common troubleshooting scenarios
8.5.1 Authentication failures
8.5.2 API errors (4xx responses)
8.5.3 Rate limiting
8.5.4 Checking your configuration
8.6 Debugging queries
8.7 Inspecting responses
8.8 Using parameterize for testing
8.9 Getting help
9.3

8 Logging & Debugging🔗

When something isn’t working as expected, you need visibility into what bcnav is doing. This tutorial covers bcnav’s logging system and techniques for troubleshooting API issues.

8.1 Enabling logging🔗

bcnav uses Racket’s logging system to report what it’s doing. By default, log messages aren’t displayed. To see them, start a log listener:

(start-bc-listener)

Now you’ll see log messages in the REPL as bcnav operates:

> (bc-authenticate!)

(info) Authenticating with client credentials for tenant: contoso.onmicrosoft.com

(info) Authentication successful. Token expires at 1704567890

#<bc-token>

8.2 Log levels🔗

bcnav produces messages at different severity levels:

Level

What it includes

debug

Detailed internal operations, HTTP requests/responses

info

Normal operations like authentication, cache hits

warning

Recoverable issues like rate limiting with retry

error

Failures that stop the operation

Start the listener at a specific level to control verbosity:

;; Show only info and above (default)
(start-bc-listener 'info)
 
;; Show everything, including debug details
(start-bc-listener 'debug)
 
;; Show only warnings and errors
(start-bc-listener 'warning)

8.3 Request logging🔗

For detailed HTTP debugging, enable request logging:

(current-bc-log-requests #t)
(start-bc-listener 'debug)

Now you’ll see each HTTP request and response:

> (customers #:query (make-query #:top 2))

(debug) GET https://api.businesscentral.dynamics.com/v2.0/.../customers?$top=2

(debug) GET https://api.businesscentral.dynamics.com/... -> OK

'(#hasheq(...) #hasheq(...))

This is invaluable for:
  • Verifying your queries are being constructed correctly

  • Seeing the actual URLs being called

  • Diagnosing authentication or network issues

8.4 Stopping the listener🔗

When you’re done debugging, stop the listener:

(stop-bc-listener)

Or just disable request logging while keeping other messages:

(current-bc-log-requests #f)

8.5 Common troubleshooting scenarios🔗

8.5.1 Authentication failures🔗

If bc-authenticate! fails, the error message usually explains why:

> (bc-authenticate!)

OAuth authentication failed

  Status: 400

  Error: invalid_client

  Description: AADSTS7000218: The request body must contain

               'client_assertion' or 'client_secret'.

 

Tip: Verify your tenant ID, client ID, and client secret are correct.

Common causes:
  • Wrong tenant: Double-check current-bc-tenant

  • Wrong client ID: Verify against Azure AD app registration

  • Wrong or expired secret: Secrets expire; check the Azure portal

  • Missing secret: current-bc-client-secret not set

8.5.2 API errors (4xx responses)🔗

API errors include the status code and response body:

> (customer "nonexistent-id")

Business Central API Error

  Request: GET https://api.businesscentral.dynamics.com/.../customers(nonexistent-id)

  Status:  404 Not Found

  Response:

{"error":{"code":"EntityNotFound","message":"The entity was not found."}}

 

Tip: Check that tenant, environment, and company are correct.

Common API errors:
  • 404 Not Found: Wrong ID, wrong entity name, or wrong company

  • 400 Bad Request: Invalid query syntax or field values

  • 401 Unauthorized: Token expired or insufficient permissions

  • 412 Precondition Failed: ETag mismatch (record was modified)

8.5.3 Rate limiting🔗

BC throttles excessive API requests. bcnav handles this automatically:

> (for ([i 1000]) (customers))

(warning) Rate limited, retrying in 1000ms...

(warning) Rate limited, retrying in 2000ms...

bcnav retries with exponential backoff (1s, 2s, 4s...) up to a configurable limit. If retries are exhausted, you’ll get an exn:fail:bcnav:rate-limit error.

You can adjust retry behavior:

;; Maximum number of retries (default: 3)
(current-bc-max-retries 5)
 
;; Base delay in milliseconds (default: 1000)
(current-bc-retry-base-delay 500)

8.5.4 Checking your configuration🔗

Verify your parameters are set correctly:

> (current-bc-tenant)

"contoso.onmicrosoft.com"

 

> (current-bc-environment)

"Production"

 

> (current-bc-company)

"12345678-1234-1234-1234-123456789012"

 

> (token-valid?)

#t

8.6 Debugging queries🔗

If a query isn’t returning expected results, check what bcnav is sending:

> (define q (make-query

              #:filter (_and (_= 'blocked #f)

                             (_contains 'displayName "test"))

              #:select '(id displayName)

              #:top 10))

 

> (query->params q)

'(($filter . "(blocked eq false and contains(displayName,'test'))")

  ($select . "id,displayName")

  ($top . "10"))

If the filter string looks wrong, the issue is in your filter expression. If it looks right, the issue might be:
  • No records match the criteria

  • Field names are incorrect (case-sensitive!)

  • Data types don’t match (string vs. boolean vs. number)

8.7 Inspecting responses🔗

When debugging, it helps to look at raw responses. bcnav returns hash tables that you can explore:

> (define cust (customer "some-id"))

> (hash-keys cust)

'(id number displayName type email phoneNumber blocked

  @odata.context @odata.etag ...)

 

> (for ([(k v) (in-hash cust)])

    (printf "~a: ~v\n" k v))

8.8 Using parameterize for testing🔗

Racket’s parameterize lets you temporarily change parameters without affecting global state:

;; Test with a different company
(parameterize ([current-bc-company "test-company-id"])
  (customers))  ;; Uses test company
 
(customers)  ;; Back to original company

This is useful for:
  • Testing against a sandbox without changing your main config

  • Temporarily enabling verbose logging

  • Disabling auto-pagination for a single call

;; One-off debug call
(parameterize ([current-bc-log-requests #t])
  (start-bc-listener 'debug)
  (customers #:query (make-query #:top 1)))

8.9 Getting help🔗

If you’re stuck:

  • Check BC’s API documentation for entity-specific requirements

  • Use inspect to verify field names and types

  • Enable debug logging to see exactly what’s being sent

  • Test the same request in a REST client (like Postman) to isolate whether the issue is in bcnav or the API itself