An Azure service that provides an event-driven serverless compute platform.
Hi @Joe Harvey ,
the error code rules out the firewall and the identity. Azure SQL reports each of those failures with its own number, and none of them is 40613:
- A client IP that no firewall rule covers returns 40615 (listed under "Cannot connect to server due to firewall issues" on the troubleshooting page below).
- Public network access set to Disabled returns 47073 (documented on the connectivity settings page).
- A token that cannot be mapped to a login returns 18456.
40613 sits in the table of transient fault error codes on Troubleshoot connectivity issues and other errors. The service returns it when the database cannot accept the session at that moment. The failure occurs before your login is evaluated against the database, so the checks you ran (DNS, TCP 1433, token acquisition, the external user and its roles) do not touch the failing step. That is also why the Proxy connection policy made no difference.
For a serverless database with auto-pause enabled, this is the documented behaviour of the pause/resume cycle. The serverless FAQ states that the first connection attempt to a paused database resumes it and that applications may receive 40613 until the resume completes. The serverless overview puts resume latency at around one minute and pause latency at one to ten minutes after the delay expires.
Each of your diagnostics was a short Function invocation without retry logic. The likely sequence: the invocation wakes the database, receives 40613, and exits before the resume finishes. By the time you look at the portal or connect from your laptop, the database is Online again, and your own sqlcmd session keeps it that way for a while. The status on the Overview blade is a point-in-time value, so the observation "Online" only holds for the moment you read it.
Two things to test, in this order:
1. Capture the database state at the moment of failure. Trigger the Function and read the status on the Overview blade within a few seconds, or watch the Resuming transition. If the status was Paused or Resuming while the Function received 40613, the cause is confirmed.
2. Retry the initial login in code. The ODBC keywords ConnectRetryCount and ConnectRetryInterval cover reconnection of a broken idle connection; they do not retry a failed initial login, so they will not help here. The retry has to live in the Function:
import time
import pyodbc
def connect_with_retry(conn_str, attrs, delays=(5, 10, 20, 30)):
for delay in delays:
try:
return pyodbc.connect(conn_str, attrs_before=attrs)
except pyodbc.Error as e:
if "40613" not in str(e):
raise
time.sleep(delay)
return pyodbc.connect(conn_str, attrs_before=attrs)
The delays follow Microsoft's guidance for cloud clients: a first retry after 5 seconds, then growing delays with 60 seconds as the upper bound. The tuple above stops at 30 on purpose. Flex Consumption has a default function timeout of 30 minutes, but an HTTP-triggered function must respond within 230 seconds regardless of that setting, and the retries plus the login timeout of each attempt have to fit inside that window. If your trigger is a timer or a queue, you can extend the tuple.
If the Function connects on the second or third attempt, you have both the diagnosis and the permanent fix. A serverless database with auto-pause produces 40613 on the first connection after a pause, whatever the hosting plan of the client, so this is a property of the database tier rather than of Flex Consumption.
If 40613 persists across all retries while the status reads Online throughout, the pause cycle is ruled out. The remaining causes are backend reconfiguration events that are not visible from the client, and that is where the session tracing ID belongs in a support request.
One simplification you may want regardless of the outcome: ODBC Driver 18 accepts Authentication=ActiveDirectoryMsi in the connection string and acquires the managed identity token itself. That removes the manual token struct in attrs_before. The two approaches cannot be combined; the driver rejects an access token together with the Authentication keyword.
On your last question: VNet integration with a service endpoint or a private endpoint changes which source IP the gateway sees. 40613 is not an IP-based rejection, so neither would change the outcome. The configuration you have (managed identity, IP firewall rules, no "Allow Azure services") is sound for this scenario once the client retries.
References
- Troubleshoot connectivity issues and other errors
- Azure SQL Database serverless FAQ
- Serverless compute tier for Azure SQL Database
- Connectivity settings for Azure SQL Database
- Azure Functions scale and hosting
- Azure Functions Flex Consumption plan hosting
Drafted with help from Claude, disclosed per the Q&A AI usage policy. All statements were checked against the Microsoft Learn pages linked above.