fluent/fluentd · error · ArgumentError
key must be a string (or symbol for to_s)
Error message
key must be a string (or symbol for to_s)
What it means
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.
Source
Thrown at lib/fluent/plugin/storage.rb:35
require 'fluent/plugin/base'
require 'fluent/plugin/owned_by_mixin'
module Fluent
module Plugin
class Storage < Base
include OwnedByMixin
DEFAULT_TYPE = 'local'
configured_in :storage
config_param :persistent, :bool, default: false # load/save with all operations
config_param :autosave, :bool, default: true
config_param :autosave_interval, :time, default: 10
config_param :save_at_shutdown, :bool, default: true
def self.validate_key(key)
raise ArgumentError, "key must be a string (or symbol for to_s)" unless key.is_a?(String) || key.is_a?(Symbol)
key.to_s
end
attr_accessor :log
def persistent_always?
false
end
def synchronized?
false
end
def implementation
self
end
def loadView on GitHub (pinned to dd45c6e18d)
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').
Example fix
# before
@storage.put(record['status'], 1) # Integer key -> ArgumentError
# after
@storage.put("status_#{record['status']}", 1) Defensive patterns
Strategy: type-guard
Validate before calling
key = key.to_s if key.is_a?(Symbol)
key = "key_#{key}" unless key.is_a?(String)
@storage.put(key, value) Type guard
def valid_storage_key?(k) k.is_a?(String) || k.is_a?(Symbol) end
Try / catch
rescue ArgumentError => e raise unless e.message =~ /key must be a string/ retry with key.to_s
Prevention
- 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}").
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: Invalid argument type errors: "must be of type string", "expected X, got Y", and ERR_INVALID_ARG_TYPE explained — this error's family across 15 libraries.
Related errors
- Implement this method in child class
- tag must be a String: #{tag.class}
- time must be a Fluent::EventTime (or Integer): #{time.class}
- record must be a Hash: #{record.class}
- Implement this method in child class
AI-assisted analysis of fluent/fluentd@dd45c6e18d (2026-08-21).
Data as JSON: /api/errors/cba199ec48929b24.
Report an issue: GitHub.