opf/openproject · error · Setting::NotWritableError
#{name} is not writable but can be set through env vars or c
Error message
#{name} is not writable but can be set through env vars or configuration.yml file. What it means
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.
Source
Thrown at app/models/setting.rb:122
def nullable_integer_format?
format == :integer && definition.default.nil?
end
def non_null_integer_format?
format == :integer && !definition.default.nil?
end
def value
self.class.deserialize(name, read_attribute(:value))
end
def value=(val)
set_value! val
end
def set_value!(val, force: false)
unless force || definition.writable?
raise NotWritableError, "#{name} is not writable but can be set through env vars or configuration.yml file."
end
self[:value] = formatted_value(val)
end
def formatted_value(value)
return value if value.blank?
if definition.serialized?
return value.to_yaml
end
value.to_s
end
# Returns the value of the setting named name
def self.[](name)
cached_or_default(name)View on GitHub (pinned to d9742c43f3)
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.
Example fix
# before
Setting.foo = 'bar' # raises NotWritableError
# after
# config/configuration.yml or ENV:
# OPENPROJECT_FOO=bar
# then restart. Or, in a one-off maintenance task:
Setting.find_by!(name: 'foo').set_value!('bar', force: true) Defensive patterns
Strategy: validation
Validate before calling
definition = Setting.definitions[name.to_sym] raise Setting::NotWritableError, name unless definition.nil? || definition.writable?
Type guard
def writable_setting?(name) Setting.definitions[name.to_sym]&.writable? end
Try / catch
begin
Setting.foo = value
rescue Setting::NotWritableError
instruct_env_configuration('OPENPROJECT_FOO')
end Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Value for #{name} must be one of #{allowed.join(', ')} but i
- Configuration value for environment variable '#{env_var_name
- Invalid API token. Please check your credentials in the conf
- LDAP-Error: Could not authenticate at the LDAP-Server.
- LDAP-Error: %{error_message}
AI-assisted analysis of opf/openproject@d9742c43f3 (2026-08-21).
Data as JSON: /api/errors/d1c8a9a03b3abbbb.
Report an issue: GitHub.