{"record":{"id":"cba199ec48929b24","repo":"fluent/fluentd","slug":"key-must-be-a-string-or-symbol-for-to-s","errorCode":null,"errorMessage":"key must be a string (or symbol for to_s)","messagePattern":"key must be a string \\(or symbol for to_s\\)","errorType":"exception","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"lib/fluent/plugin/storage.rb","lineNumber":35,"sourceCode":"require 'fluent/plugin/base'\nrequire 'fluent/plugin/owned_by_mixin'\n\nmodule Fluent\n  module Plugin\n    class Storage < Base\n      include OwnedByMixin\n\n      DEFAULT_TYPE = 'local'\n\n      configured_in :storage\n\n      config_param :persistent,        :bool, default: false # load/save with all operations\n      config_param :autosave,          :bool, default: true\n      config_param :autosave_interval, :time, default: 10\n      config_param :save_at_shutdown,  :bool, default: true\n\n      def self.validate_key(key)\n        raise ArgumentError, \"key must be a string (or symbol for to_s)\" unless key.is_a?(String) || key.is_a?(Symbol)\n        key.to_s\n      end\n\n      attr_accessor :log\n\n      def persistent_always?\n        false\n      end\n\n      def synchronized?\n        false\n      end\n\n      def implementation\n        self\n      end\n\n      def load","sourceCodeStart":17,"sourceCodeEnd":53,"githubUrl":"https://github.com/fluent/fluentd/blob/dd45c6e18dc7be33b5e5a0f0767bf46307ff5626/lib/fluent/plugin/storage.rb#L17-L53","documentation":"Fluent::Plugin::Storage.validate_key (lib/fluent/plugin/storage.rb:35) raises ArgumentError unless the key is a String or Symbol (it returns key.to_s on success). It is the contract helper that storage plugin implementations (and code using plugin storage, e.g. storage_local or counter plugins) should apply before touching the backing store, keeping the on-disk/hash namespace string-keyed. Integer keys, nil, or arbitrary objects fail fast here rather than corrupting storage.","triggerScenarios":"A custom storage plugin calling Storage.validate_key(key) in get/put/fetch and receiving 1, nil, or an object from record data; passing a JSON-parsed integer (e.g. response codes) as a key; nil keys from Hash#[] misses used unchecked.","commonSituations":"Custom storage plugin development; using plugin storage to count per-status events with integer status codes as keys; interpolating nil when a lookup misses; porting v0-era code that assumed auto-conversion.","solutions":["Convert keys at the boundary: storage.put(key.to_s, value) or use String keys from the start.","Guard nil: key = something || 'default' before use.","In your storage plugin subclass, keep calling validate_key — it enforces the contract — but normalize callers upstream.","For numeric identifiers, use explicit string formatting ('status_200')."],"exampleFix":"# before\n@storage.put(record['status'], 1)   # Integer key -> ArgumentError\n# after\n@storage.put(\"status_#{record['status']}\", 1)","handlingStrategy":"type-guard","validationCode":"key = key.to_s if key.is_a?(Symbol)\nkey = \"key_#{key}\" unless key.is_a?(String)\n@storage.put(key, value)","typeGuard":"def valid_storage_key?(k)\n  k.is_a?(String) || k.is_a?(Symbol)\nend","tryCatchPattern":"rescue ArgumentError => e\n  raise unless e.message =~ /key must be a string/\n  retry with key.to_s\n","preventionTips":["Normalize keys with to_s at the call boundary instead of relying on storage to coerce.","Never pass nil or Integers from record data directly as storage keys.","Use string interpolation for numeric identifiers (\"status_#{code}\")."],"tags":["fluentd","storage","argument-validation","plugin-development"],"backgroundTag":"invalid-argument-type","analyzedSha":"dd45c6e18dc7be33b5e5a0f0767bf46307ff5626","analyzedAt":"2026-08-21T16:22:07.332Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}