Azure function app (python) on a timer_trigger didn't trigger as expected (no new deployment)

Mikkel Gadekær Hansen 0 Reputation points
2026-08-17T06:50:37.48+00:00

Screenshot 2026-08-17 081818

(regarding the image, the errors aren't relevant for this case, that's something on our end)
We have a function app running on a timer_trigger in python and for some reason it didn't trigger on it's own on the 15th and 16th at 3UTC (5CET) as it would be expected and I can't seem to see any logs that tell me why that is.

Example of how the function is defined:

@app.function_name(name="function_name")
@app.timer_trigger(arg_name="timer", schedule="0 0 3 * * *", type="timer", run_on_startup=False)
def run_my_function(timer: func.TimerRequest) -> None:
   logging.info(f"function executed at: {datetime.datetime.now()}")

When I look across the function app as a whole there are several other functions that have been working just fine in the same timeframe and there was no deployment between the 14th (after it worked) and 17th (where it triggered again). So the entire 15th(no auto trigger), 16th(no auto trigger) and 17th(triggered automatically as it should) have run on the same deployment.

Azure Functions
Azure Functions

An Azure service that provides an event-driven serverless compute platform.


1 answer

Sort by: Most helpful
  1. Fabian 80 Reputation points
    2026-08-20T13:31:06.84+00:00

    Hi @Mikkel Gadekær Hansen ,

    Start with the weekdays in your own screenshot. The 8th and the 15th are both Saturdays, and neither ran. The 16th, a Sunday, didn't run either. The one Sunday that did run is the 9th. Every Saturday in that window is missing, so pull a longer range before calling it coincidence.

    What you can eliminate

    The error on the 14th didn't stop the schedule. The docs are explicit: "the timer trigger doesn't retry after a function fails. When a function fails, it isn't called again until the next time on the schedule."

    A dead Python worker doesn't explain it either. The timer machinery lives in the Functions host, not in the Python worker: the host decides an occurrence is due, then hands the invocation to the worker. If the worker dies, the occurrence still fires and lands as a failed invocation. That explains your two errors, never the silence. It also means nothing in function_app.py can cause this, and nothing you change there will fix it.

    A stuck lease doesn't last two days either. The host coordinates the timer through a blob lease of at most 60 seconds, renewed while it holds it, and it re-checks about once a minute if it can't take it.

    That leaves two candidates: the host wasn't running the listener at all on the 15th and 16th, or the persisted schedule state was wrong. The second is checkable and I'll come back to it.

    The hypothesis that fits

    Without Always On, "the Functions runtime goes idle after a few minutes of inactivity" on an App Service plan, and you reactivate it either with an HTTP request or by accessing "your app in the Azure portal" (dedicated plan). An idle runtime has no listener, so the occurrence passes unobserved. Consumption scale-to-zero and a stopped app produce the same silence.

    This exact symptom has been reported on Python before: a timer trigger that only fires when the portal is opened or an HTTP trigger arrives, with IsPastDue in the trigger details when it does.

    That's why your off-schedule rows matter. The scheduled run failed on Friday at 05:00, a successful run follows at 08:56 the same morning, and two more failures land on Saturday morning. That reads like someone working the incident by hand rather than like routine traffic. Either way, the app was being touched at those moments and not at 03:00, which is what an idle app looks like from the outside.

    Check each of those three rows on its own, because a cold start and a manual click produce two invocations in quick succession: opening the Functions blade or hitting Run calls the admin API, which starts the host, which fires the missed occurrence as past due, and then the click itself adds a second invocation. The 09:36:36 and 09:42:07 pair on the 15th has exactly that shape.

    The trigger details tell them apart. A catch-up carries UnscheduledInvocationReason: IsPastDue with OriginalSchedule. A portal Test/Run or POST /admin/functions/{name} is logged with Reason='This function was programmatically called via the host APIs.' and never touches the schedule state.

    What's already in your telemetry

    No log-level change needed. These messages are logged at Information or Error and should be there now:

    traces
    | where timestamp >= datetime(2026-08-07) and timestamp < datetime(2026-08-19)
    | where message contains "listener"
        or message contains "timer trigger status"
        or message contains "IsPastDue"
        or message contains "Host started"
    | project timestamp, cloud_RoleInstance, message, Category = tostring(customDimensions.Category)
    | order by timestamp asc
    

    Use contains, not has. has matches whole terms, so a multi-word fragment like "timer trigger status" silently returns nothing and the query looks like it worked. cloud_RoleInstance is the useful column: a change across the gap means the host was recycled. The exception detail for your two failures is in exceptions, not traces.

    The artifact that settles it

    The status blob, in the azure-webjobs-hosts container. The Host.Functions. segment is the generated type name the host creates for every function regardless of language, so the path isn't just your function name:

    timers/<host id>/Host.Functions.<function name>/status
    

    Your function name follows verbatim, hyphens and underscores included. If you can't find it, list timers/<host id>/ and take what's there.

    Last = the 14th at 03:00 with Next = the 15th at 03:00 means the schedule state was intact and the host wasn't there. A Next that jumped forward means the state itself was wrong. LastUpdated against your 08:56 and 09:36 rows tells you whether those went through the listener.

    Rule-outs

    0 0 3 * * * is 03:00 UTC and the table showing 05:00 is CEST, so the cron is right. Indexing is fine, since a function_app.py that fails to import takes down every function defined in it, and the others kept running. Check for an AzureWebJobs.<function name>.Disabled app setting and confirm WEBSITE_TIME_ZONE is unset.

    What would settle it

    The hosting plan and, on Dedicated, whether Always On is on. Whether those off-schedule rows report IsPastDue. Whether cloud_RoleInstance changed across the gap. And whether a longer range confirms every Saturday is missing.

    References


    Drafted with help from Claude, disclosed per the Q&A AI usage policy. The retry behaviour, the idle behaviour without Always On, and the status blob layout were verified against the linked timer trigger and Dedicated plan pages and the timer extension's schedule monitor before posting.

    Was this answer helpful?


Your answer

Answers can be marked as 'Accepted' by the question author and 'Recommended' by moderators, which helps users know the answer solved the author's problem.