Cannot coerce a set to a string

Hello, I’m trying to build sddm theme. This is the error I’m having but don’t know what to fix

 … while evaluating derivation 'sugar-candy'
         whose name attribute is located at /nix/store/frfyxcpzsdasdin76x83krbhpgkis8b0-source/pkgs/stdenv/generic/make-derivation.nix:336:7

       … while evaluating attribute 'themeIni' of derivation 'sugar-candy'
         at /nix/store/i92lhn3m5jax21lp7rq9ahipc1zd31pp-source/nixos/desktop/sddm-sugar-candy/default.nix:24:3:
           23|
           24|   themeIni = [
             |   ^
           25|     { section = "General"; key = "Background"; value = toString ./backgrounds/bg1.jpg; }

       … while evaluating one element of the list

       error: cannot coerce a set to a string: { key = "Background"; section = "General"; value = "/nix/store/i92lhn3m5jax21lp7rq9ahipc1zd31pp-source/nixos/desktop/sddm-sugar-candy/backgrounds/bg1.jpg"; }

I have isolate the problem to this:

  themeIni = [
    { section = "General"; key = "Background"; value = toString ./backgrounds/bg1.jpg; }
    { section = "General"; key = "ScreenHeight"; value = "1080"; }
    { section = "General"; key = "ScreenWidth"; value = "1920"; }
    { section = "General"; key = "BlurRadius"; value = "50"; }
    { section = "General"; key = "ForceHideCompletePassword"; value = "true"; }
    { section = "General"; key = "HeaderText"; value = "Glory To Mankind!"; }
    { section = "General"; key = "AccentColor"; value = "#d2f8d2";}
  ];

buildCommand = ''
    ${lib.concatMapStringsSep "\n" (i: ''crudini --set --inplace $dir/theme.conf "${i.section}" "${i.key}" "${i.value}"'') finalAttrs.themeIni}
  '';

My intention is to have a set of labeled strings, which i will then use the crudini command with
So for each item in the set, I would use it to construct a command like:

crudini --set --inplace $dir/theme.conf "General" "Background" "/nix/store/i92lhn3m5jax21lp7rq9ahipc1zd31pp-source/nixos/desktop/sddm-sugar-candy/backgrounds/bg1.jpg"

this is the whole build file, if you are interested:

{
stdenv
, lib
, fetchFromGitHub
, crudini
}:

stdenv.mkDerivation (finalAttrs: {
  name = "sugar-candy";
  pname = "sddm-theme-${finalAttrs.name}";
  version = "1.6";

  src = fetchFromGitHub {
    owner = "Kangie";
    repo = "sddm-${finalAttrs.name}";
    rev = "v${finalAttrs.version}";
    sha256 = "";
  };

  nativeBuildInputs = [
    crudini
  ];

  themeIni = [
    { section = "General"; key = "Background"; value = toString ./backgrounds/bg1.jpg; }
    { section = "General"; key = "ScreenHeight"; value = "1080"; }
    { section = "General"; key = "ScreenWidth"; value = "1920"; }
    { section = "General"; key = "BlurRadius"; value = "50"; }
    { section = "General"; key = "ForceHideCompletePassword"; value = "true"; }
  ];

  buildCommand = ''
    dir=$out/share/sddm/themes/${finalAttrs.name}
    doc=$out/share/doc/${finalAttrs.pname}

    mkdir -p $dir $doc
    if [ -d $src/${finalAttrs.name} ]; then
      srcDir=$src/${finalAttrs.name}
    else
      srcDir=$src
    fi
    cp -r $srcDir/* $dir/
    for f in $dir/{AUTHORS,COPYING,LICENSE,README,*.md,*.txt}; do
      test -f $f && mv $f $doc/
    done

    chmod 644 $dir/theme.conf

    ${lib.concatMapStringsSep "\n" (i: ''crudini --set --inplace $dir/theme.conf "${i.section}" "${i.key}" "${i.value}"'') finalAttrs.themeIni}
  '';
})

Why not just use the standard lib.generators.toINI/pkgs.formats.ini instead of reimplementing the wheel?
Also, don’t use buildCommand ever, use the regular phases like buildPhase, installPhase, etc. And you don’t want to operate on $src, the source files are unpacked to the current dir, so take it from there.

About this specifically: Nix (or rather mkDerivation/builtins.derivation I guess?) will try to turn all attributes into environment variables (this is why you can do things like referring to $src), as opposed to lists there’s no conversion defined for attribute sets (builtins.toString has the same behavior).

Sure, unless __structuredAttrs = true; , then it will still create envvars, but won’t try to coerce them to strings, but to me it seemed massively missing the point to answer the X-Y directly