Skip to content

Flex Consumption: ARM accepts uncatalogued functionAppConfig.runtime.version (e.g. "10" instead of "10.0"); app then fails only at host startup with generic InternalServerError #2666

Description

Summary

Creating a Flex Consumption function app with a functionAppConfig.runtime.version value that is not in the regional runtime catalog (e.g. "10" where the catalog value is "10.0" for dotnet-isolated) succeeds at ARM PUT time with no error or warning. The resulting app is permanently broken in the most opaque way possible:

  • every instance start fails; the app URL never responds (connection timeout),
  • POST .../host/default/listkeys returns 400 BadRequest: "Encountered an error (InternalServerError) from host runtime.",
  • zip deployments upload successfully but end with "Function triggers synchronization failed due to Response status code does not indicate success: 500 (Internal Server Error)",
  • nothing anywhere: portal, ARM, deployment logs; points at the runtime version.

Diagnosing this took a full team-day, because every symptom points at deployment, storage, RBAC, or app code instead at the one-character config difference.

Repro

Reproduces with any client that passes the raw string through to ARM (Bicep, ARM template, Terraform azurerm/azapi, az resource create). Note az functionapp create does NOT repro; the CLI validates and normalizes client-side (see "Evidence" below).

  1. Create a Flex Consumption app (Bicep or az resource) with:
    "functionAppConfig": {
      "runtime": { "name": "dotnet-isolated", "version": "10" }
    }
    in a region where the catalog value is "10.0" (e.g. West Europe; verify with
    az functionapp list-flexconsumption-runtimes --location westeurope --runtime dotnet-isolated).
  2. Observe: creation succeeds. Portal shows a healthy-looking app.
  3. Deploy any valid dotnet-isolated package (zip deploy). Upload succeeds; deployment ends status: 6, "Function triggers synchronization failed ... 500".
  4. az rest --method post --url ".../sites/<app>/host/default/listkeys?api-version=2023-12-01" -> 400 BadRequest / "Encountered an error (InternalServerError) from host runtime."
  5. Patch the resource: runtime.version -> "10.0". Redeploy. Everything works immediately. No other change.

Expected behavior

Any one of:

  1. Reject at PUT with a 400 naming the property and the valid values. The RP demonstrably already does this for some invalid versions: creating with python "V2" returns "Site.FunctionAppConfig.Runtime.Version is invalid. The specified value of runtime version 'V2' for runtime name 'python' is not supported." (quoted in function_app_flex_consumption - Update allowed python versions hashicorp/terraform-provider-azurerm#31154). So a validation allow-list exists; bare-major dotnet values ("10") fall through a hole in it.
  2. Normalize bare-major versions to the catalog form ("10""10.0"), which is exactly what azure-cli does client-side (_FlexFunctionAppStackRuntimeHelper in appservice/custom.py fetches the regional functionAppStacks?sku=FC1 catalog, matches version variants, and substitutes the canonical string into the request).
  3. Fail specifically at provision time. If the value is accepted, the host/platform knows at instance start that it cannot resolve a worker image for dotnet-isolated|10; surfacing that (instead of a generic InternalServerError from host runtime through three different APIs) would make the failure self-explanatory.

Additional evidence the current contract is a trap

  • The Microsoft.Web swagger (CommonDefinitions.json, stable/2024-04-01 and 2024-11-01) types FunctionsRuntime.version as an unconstrained nullable string whose own description is "Function app runtime version. Example: 8 (for dotnet-isolated)"; i.e. the spec's own example uses the bare-major format that bricks the app (catalog value is 8.0).
  • The official Flex Bicep sample (Azure-Samples/azure-functions-flex-consumption-samples, IaC/bicep/main.bicep) constrains runtime.name with @allowed(...) but leaves the version a free string.
  • Terraform azurerm_function_app_flex_consumption validates runtime_name (StringInSlice) but runtime_version only as non-empty, so IaC users pass the raw string to ARM.
  • ARM/Bicep template reference documents runtime.name as an enum but version as a free string.

Related (not duplicates)

Environment

  • Region: West Europe; observed 2026-08-13.
  • Runtime: dotnet-isolated; catalog value "10.0", accepted invalid value "10".
  • Plan: Flex Consumption (FC1), Linux.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions