{"record":{"id":"74cd311f41499f49","repo":"basecamp/kamal","slug":"accessories-name-unknown-roles-missing-roles","errorCode":null,"errorMessage":"accessories/#{name}: unknown roles #{missing_roles.join(\", \")}","messagePattern":"accessories/#(.+?): unknown roles #(.+?)","errorType":"validation","errorClass":"Kamal::ConfigurationError","httpStatus":null,"severity":"error","filePath":"lib/kamal/configuration/accessory.rb","lineNumber":275,"sourceCode":"    end\n\n    def extract_hosts_from_config_with_tag(tag)\n      if (servers_with_roles = config.raw_config.servers).is_a?(Hash)\n        servers_with_roles.flat_map do |role, servers_in_role|\n          servers_in_role.filter_map do |host|\n            host.keys.first if host.is_a?(Hash) && host.values.first.include?(tag)\n          end\n        end\n      end\n    end\n\n    def network\n      accessory_config[\"network\"] || DEFAULT_NETWORK\n    end\n\n    def ensure_valid_roles\n      if accessory_config[\"roles\"] && (missing_roles = accessory_config[\"roles\"] - config.roles.map(&:name)).any?\n        raise Kamal::ConfigurationError, \"accessories/#{name}: unknown roles #{missing_roles.join(\", \")}\"\n      elsif accessory_config[\"role\"] && !config.role(accessory_config[\"role\"])\n        raise Kamal::ConfigurationError, \"accessories/#{name}: unknown role #{accessory_config[\"role\"]}\"\n      end\n    end\nend\n","sourceCodeStart":257,"sourceCodeEnd":281,"githubUrl":"https://github.com/basecamp/kamal/blob/eee0083b38661c3707c6b6052cc89e85038a096c/lib/kamal/configuration/accessory.rb#L257-L281","documentation":"Kamal::Configuration::Accessory#ensure_valid_roles validates an accessory's `roles:` array (the roles whose hosts the accessory should run on) against the names of roles actually defined under `servers:`. Any name not found there is collected and reported in this Kamal::ConfigurationError, prefixed with the accessory name so you know which YAML block is at fault.","triggerScenarios":"An accessory in config/deploy.yml declaring `roles: [ \"sidekiq\" ]` when servers define `workers:` instead; renaming a role without updating accessories referencing it; a plural `roles:` array containing one valid and one misspelled role name — the missing ones are listed in the message.","commonSituations":"Role renames during refactors that miss accessory references; copy-pasted accessory configs from another project with different role names; singular/plural confusion between `role` and `roles` keys.","solutions":["Align the accessory's `roles:` entries with the exact keys under `servers:` (check spelling and singular/plural).","If the role was renamed, update the accessory to the new name.","Run `kamal config` to list the actual role names before fixing the accessory."],"exampleFix":"# config/deploy.yml — before\nservers:\n  web:\n    - 1.2.3.4\n  workers:\n    - 1.2.3.5\naccessories:\n  redis:\n    roles: [ \"sidekiq\" ]\n\n# after\nservers:\n  web:\n    - 1.2.3.4\n  workers:\n    - 1.2.3.5\naccessories:\n  redis:\n    roles: [ \"workers\" ]","handlingStrategy":"validation","validationCode":"def accessory_roles_valid?(raw)\n  role_names = raw[\"servers\"].is_a?(Array) ? [\"web\"] : raw[\"servers\"].keys.map(&:to_s)\n  raw[\"accessories\"].to_h.all? do |_name, cfg|\n    Array(cfg[\"roles\"]).all? { |r| role_names.include?(r.to_s) }\n  end\nend","typeGuard":null,"tryCatchPattern":"begin\n  config = Kamal::Configuration.new(create_config_files: false)\nrescue Kamal::ConfigurationError => e\n  puts \"Deploy config invalid: #{e.message}\"\n  exit 1\nend","preventionTips":["After renaming a servers role, grep accessories for the old name.","List real roles with `kamal config` before writing accessory role references.","Prefer `roles:` (array) for clarity over singular `role:` in new configs."],"tags":["kamal","accessories","roles","configuration"],"backgroundTag":"config-reference-validation-failed","analyzedSha":"eee0083b38661c3707c6b6052cc89e85038a096c","analyzedAt":"2026-08-21T15:17:22.045Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}