{"record":{"id":"d1c8a9a03b3abbbb","repo":"opf/openproject","slug":"name-is-not-writable-but-can-be-set-through-env","errorCode":null,"errorMessage":"#{name} is not writable but can be set through env vars or configuration.yml file.","messagePattern":"#(.+?) is not writable but can be set through env vars or configuration\\.yml file\\.","errorType":"exception","errorClass":"Setting::NotWritableError","httpStatus":null,"severity":"error","filePath":"app/models/setting.rb","lineNumber":122,"sourceCode":"  def nullable_integer_format?\n    format == :integer && definition.default.nil?\n  end\n\n  def non_null_integer_format?\n    format == :integer && !definition.default.nil?\n  end\n\n  def value\n    self.class.deserialize(name, read_attribute(:value))\n  end\n\n  def value=(val)\n    set_value! val\n  end\n\n  def set_value!(val, force: false)\n    unless force || definition.writable?\n      raise NotWritableError, \"#{name} is not writable but can be set through env vars or configuration.yml file.\"\n    end\n\n    self[:value] = formatted_value(val)\n  end\n\n  def formatted_value(value)\n    return value if value.blank?\n\n    if definition.serialized?\n      return value.to_yaml\n    end\n\n    value.to_s\n  end\n\n  # Returns the value of the setting named name\n  def self.[](name)\n    cached_or_default(name)","sourceCodeStart":104,"sourceCodeEnd":140,"githubUrl":"https://github.com/opf/openproject/blob/d9742c43f3424c34b63550f8c03f201fe5c3040c/app/models/setting.rb#L104-L140","documentation":"Setting#set_value! writes a setting value only when its definition declares it writable (or force: true is passed). Settings whose definition has writable: false — typically those fed from ENV variables or config/configuration.yml, such as database or delivery-related values — raise NotWritableError on assignment. The database copy stays authoritative only for writable settings; the others are provisioned externally.","triggerScenarios":"Calling Setting.some_setting = value (or setting settings via the API/console) for a setting defined with writable: false — e.g. after moving a mailer or storage setting behind an environment variable.","commonSituations":"Upgrades that convert formerly writable settings to env-var-managed ones while old deploy scripts/seeds keep assigning them; operators trying to change behavior from the admin UI or console without realizing the value comes from configuration.yml.","solutions":["Set the value through its real source: the corresponding ENV variable or config/configuration.yml, then restart the application.","Inspect the definition to confirm: Setting.definitions / the setting's writable? flag tells you which side owns it.","Only if you truly must override in a migration/maintenance task, call set_value!(value, force: true) deliberately — normal code should never force."],"exampleFix":"# before\nSetting.foo = 'bar'   # raises NotWritableError\n\n# after\n# config/configuration.yml or ENV:\n#   OPENPROJECT_FOO=bar\n# then restart. Or, in a one-off maintenance task:\nSetting.find_by!(name: 'foo').set_value!('bar', force: true)","handlingStrategy":"validation","validationCode":"definition = Setting.definitions[name.to_sym]\nraise Setting::NotWritableError, name unless definition.nil? || definition.writable?","typeGuard":"def writable_setting?(name)\n  Setting.definitions[name.to_sym]&.writable?\nend","tryCatchPattern":"begin\n  Setting.foo = value\nrescue Setting::NotWritableError\n  instruct_env_configuration('OPENPROJECT_FOO')\nend","preventionTips":["Audit deploy scripts/seeds after upgrades for assignments to settings that became env-managed.","Check definition.writable? before assigning; route non-writable names to ENV/config guidance.","Reserve force: true for explicit one-off migrations and log every forced write."],"tags":["configuration","settings","env-vars","deployment"],"backgroundTag":"read-only-configuration","analyzedSha":"d9742c43f3424c34b63550f8c03f201fe5c3040c","analyzedAt":"2026-08-21T14:40:06.829Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}