Using expansion syntax :+ in app's LD_LIBRARY_PATH

I’m trying to understand best practice for setting LD_LIBRARY_PATH in apps.foobar.environment. From my testing I was surprised to learn that expansion syntax like the following does not work the way I expected as it seems to remove some of the default paths that are generally set for apps.

apps:
  foobar:
    command: ...
    environment:
       LD_LIBRARY_PATH: $SNAP/usr/my-lib${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}

The following demonstrates this:

name: test-snap
base: core24
version: '1.0.0'
summary: Test snap
description: |
  LD_LIBRARY_PATH test
grade: devel
confinement: strict

apps:
  print-path:
    command: usr/bin/print-path

parts:
  print-path:
    plugin: dump
    source: bin/
    organize:
      print-path: usr/bin/print-path

where print-path is a simple script:

#!/bin/bash

echo "LD_LIBRARY_PATH: ${LD_LIBRARY_PATH}"

This results in:

$ test-snap.print-path 
LD_LIBRARY_PATH: /var/lib/snapd/lib/gl:/var/lib/snapd/lib/gl32:/var/lib/snapd/void

As you can see, there are some default directories added for an app, and I assume it’s important that these be preserved even if a user wants to update LD_LIBRARY_PATH (more on that later…). Note that if you stage a package (e.g. libcurl4) you end up with even more defaults:

$ test-snap.print-path 
LD_LIBRARY_PATH: /var/lib/snapd/lib/gl:/var/lib/snapd/lib/gl32:/var/lib/snapd/void:/snap/test-snap/x12/usr/lib:/snap/test-snap/x12/usr/lib/x86_64-linux-gnu

So LD_LIBRARY_PATH is automatically updated to find the libs from stage-packages, which is a nice feature.

Now if I adjust the LD_LIBRARY_PATH from the apps.print-path.environment section using the suggested method from the snapcraft documentation:

apps:
  print-path:
    command: usr/bin/print-path
    environment:
      LD_LIBRARY_PATH: $SNAP/foo/bar:${LD_LIBRARY_PATH}

This produces the expected result:

$ test-snap.print-path 
LD_LIBRARY_PATH: /snap/test-snap/x13/foo/bar:/var/lib/snapd/lib/gl:/var/lib/snapd/lib/gl32:/var/lib/snapd/void:/snap/test-snap/x13/usr/lib:/snap/test-snap/x13/usr/lib/x86_64-linux-gnu

However, if I try using the :+ parameter, which I see used frequently in snap definitions and is suggested as being important for avoiding unbound variable definitions (albeit for build-environment in this particular link), I get an unexpected result:

apps:
  print-path:
    command: usr/bin/print-path
    environment:
      LD_LIBRARY_PATH: $SNAP/foo/bar${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}
$ test-snap.print-path 
LD_LIBRARY_PATH: /snap/test-snap/x14/foo/bar

So the defaults that are generally set are removed, and as a result your app can no longer find libs from stage-packages installed in $SNAP/usr/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR. And perhaps there may be other issues from other directories (/var/lib/snapd/lib/gl:/var/lib/snapd/lib/gl32:/var/lib/snapd/void) being removed?

Is this expected?

I understand that LD_LIBRARY_PATH: $SNAP/foo/bar:$LD_LIBRARY_PATH will work (and that’s what is suggested in the snapcraft docs), but I’ve also seen security concerns raised over using this approach. cc @lucyllewy

I’m curious what others think and consider best practice. Thanks!

Side note: my tests used snapcraft version 8.9.4 and snapd version 2.68.4.