Skip to content

tls: null in an override panics the render in every chart using the tri-state TLS helper #3705

Open

Description

helm template packages/apps/qdrant --set tls=null fails with nil pointer evaluating interface {}.enabled in the TLS helper. The helper reads .Values.tls.enabled to implement the tri-state where an unset value follows external, and it guards the value rather than the map, so a tls key present and explicitly null dereferences nil.

Four charts carry that helper and all four fail the same way:

$ git grep -l 'Values.tls.enabled' -- packages/apps
packages/apps/kafka/templates/_tls.tpl
packages/apps/nats/templates/_tls.tpl
packages/apps/postgres/templates/_tls.tpl
packages/apps/qdrant/templates/_helpers.tpl

values.yaml declares tls: {} in each, so a default install is safe. The failure needs an override that sets tls to null, which is what a values file written as tls: with nothing under it produces.

Same shape as the index on a nil .Values._cluster problem, and worth fixing the same way: guard the map, not the value. nats shows the two interact. It dies on index .Values._cluster "cluster-domain" with index of untyped nil before it ever reaches the TLS helper, so its TLS behaviour is only observable once _cluster is supplied, and anyone checking the four charts one by one will read that first failure as a different bug.

Note for whoever confirms this: the helper does not live in the same file in all four. Three keep it in _tls.tpl and qdrant in _helpers.tpl, so a check that opens one filename finds one chart and concludes the problem is local to it.

Activity

  1. dosubot commented on Aug 9, 2026

    @dosubot

    Good catch consolidating these. Looking at the current state of the four templates, the picture is a bit more nuanced than all four being equally broken:

    All four helpers use the same kindIs "invalid" .Values.tls.enabled guard [1] [2] [3] [4], which correctly distinguishes an unset .Values.tls.enabled from an explicit false — but none of them guard .Values.tls itself before drilling into it. When tls: null is passed, Helm coalescing wipes the tls: {} default from values.yaml, leaving .Values.tls as nil, and accessing .enabled on nil panics.

    The fix pattern you described (guard the map, not the value) is the right approach — something like:

    {{- $tls := .Values.tls | default dict -}}
    {{- if kindIs "invalid" $tls.enabled -}}
      ...
    {{- end -}}
    

    There's also a prior issue #3611 that reported this same bug specifically for NATS and mentioned Qdrant as also affected.

    Regarding the NATS _cluster interaction you noted: NATS uses index .Values._cluster "cluster-domain" without a nil guard [5], so it'll hit index of untyped nil before even reaching the TLS helper when _cluster isn't supplied. The existing safe pattern for _cluster is index (.Values._cluster | default dict) "cluster-domain" [6], which some operator charts already use.

    To reply, just mention @dosu.


    Docs are dead. Just use Dosu.

    Leave Feedback Ask Dosu about cozystack Share Dosu with your team

  2. lexfrei commented on Aug 9, 2026

    @lexfrei
    ContributorAuthor

    The mechanism in the description is off by one step. tls is not present-and-null at render time, it is absent: Helm's null semantics delete the key from the chart defaults, and they only do that when a default is declared, which values.yaml does with tls: {}. So .Values.tls is nil and the nested read panics.

    Measured alongside it: the schema is active and does reject a wrong type. --set tls=5 gives values don't meet the specifications of the schema(s), while --set tls=null passes validation and reaches the render. If the key were present carrying null, type: object would have rejected it exactly as it rejected 5. That is the evidence for "absent" rather than "present and null".

    The fix does not change: guard the map, not the value.

    Still unverified, and worth stating rather than assuming: whether an explicit null in a resource submitted to the aggregated apiserver survives to the chart values at all, or is defaulted back to {} by the structural schema first. The Helm-level barrier demonstrably does not exist; the apiserver-level one is untested.

  3. myasnikovdaniil commented on Aug 25, 2026

    @myasnikovdaniil
    Contributor

    Adding the write path side of this, since it produces the same nulls.

    cozystack-api applies schema defaults only when it converts HelmRelease to Application, that is on read (rest.go:1229). On write the spec goes into HelmRelease .spec.values as is (rest.go:1619), no defaulting and no schema validation, so tls: typed by a user reaches helm untouched and gets deleted together with the chart default, exactly as you describe.

    Where the field is required by schema the same input fails earlier: validation rejects the whole release with missing property, naming a property that the spec does contain. #3956 repairs that half on the write path, required fields only. The optional half stays here, and guarding the map in the helper also covers talos: in the kubernetes chart, which dies the same way on .Values.talos.version.

  4. added
    kind/bugCategorizes issue or PR as related to a bug
    area/databaseIssues or PRs related to managed databases (postgres, mariadb, redis, etcd, kafka, clickhouse)
    triage/acceptedIndicates an issue is ready to be actively worked on
    priority/backlogGeneral backlog priority. Lower than priority/important-longterm
    and removed
    triage/needs-triageIndicates an issue needs triage by a maintainer
    on Sep 23, 2026
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

    area/databaseIssues or PRs related to managed databases (postgres, mariadb, redis, etcd, kafka, clickhouse)kind/bugCategorizes issue or PR as related to a bugpriority/backlogGeneral backlog priority. Lower than priority/important-longtermtriage/acceptedIndicates an issue is ready to be actively worked on

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions