{"record":{"id":"1ac03f7cb5a1eafe","repo":"instructure/canvas-lms","slug":"proc-attribute-name-returned-nil-when-name-is-required","errorCode":null,"errorMessage":"Proc attribute #{name} returned nil when #{name} is required.","messagePattern":"Proc attribute #(.+?) returned nil when #(.+?) is required\\.","errorType":"validation","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"gems/event_stream/lib/event_stream/attr_config.rb","lineNumber":62,"sourceCode":"        type = options[:type]\n        typecast = CASTS[type]\n      end\n      required = !options.key?(:default)\n      unless required\n        default = options[:default]\n        default = typecast.call(name, default) if default && typecast\n        attr_config_defaults[name] = default\n      end\n\n      define_method(name) do |*args|\n        raise ArgumentError if args.length > 1\n\n        if args.empty?\n          value = attr_config_values[name]\n          if type != Proc && value.respond_to?(:call)\n            value = value.call\n            if required && value.nil?\n              raise ArgumentError, \"Proc attribute #{name} returned nil when #{name} is required.\"\n            end\n\n            return typecast.call(name, value) if typecast\n\n            return value\n          end\n          return value\n        end\n\n        value = args.first\n        if typecast && !value.respond_to?(:call)\n          value = typecast.call(name, value)\n        end\n        attr_config_values[name] = value\n      end\n    end\n  end\n","sourceCodeStart":44,"sourceCodeEnd":80,"githubUrl":"https://github.com/instructure/canvas-lms/blob/1c9f0bb8013ed69c4f2efe11fd483025469b7e6c/gems/event_stream/lib/event_stream/attr_config.rb#L44-L80","documentation":"EventStream's attr_config allows attributes whose value is a Proc resolved lazily on read. If such an attribute was declared `required` and its Proc (or callable) returns nil when read, attr_config raises ArgumentError rather than emitting events with a nil required field.","triggerScenarios":"Defining an EventStream::Stream/Index with an attribute configured via a Proc, marking it required, and the Proc returning nil at read time (e.g. the Proc reads session/context data that is absent, or reads a model field that is nil).","commonSituations":"Livestream attribute procs relying on `current_user`, `request`, or a DB record that is nil in background jobs or after object destruction; misordered initialization so the proc reads before its subject is set.","solutions":["Fix the Proc to return a non-nil value (add fallback/default inside the proc)","Remove `required: true` from the attribute if nil is actually acceptable","Guard the event emission site so the proc's input always exists","Log/inspect which attribute is named in the message and audit its data source"],"exampleFix":"// before\nattr_config :account_id, required: true, type: String\n... proc { account&.id }\n// after\n... proc { account&.id || Canvas::EventStream::NULL_ACCOUNT_ID }","handlingStrategy":"try-catch","validationCode":null,"typeGuard":"def callable_non_nil?(v)\n  v.respond_to?(:call) && !v.call.nil?\nend","tryCatchPattern":"begin\n  emit_event(stream)\nrescue ArgumentError => e\n  raise unless e.message.include?('returned nil when')\n  Rails.logger.error(\"EventStream proc returned nil: #{e.message}\")\nend","preventionTips":["Add `|| default` fallbacks inside required attribute procs","Only mark attributes required when their data always exists","Test event emission from background jobs where session data is nil"],"tags":["ruby","event-stream","lazy-attribute","argument-error"],"backgroundTag":"null-argument","analyzedSha":"1c9f0bb8013ed69c4f2efe11fd483025469b7e6c","analyzedAt":"2026-09-15T20:33:18.891Z","contentChangedAt":"2026-09-15T20:33:18.891Z","schemaVersion":2},"datasetVersion":"2026-09-23T02:17:17.105Z"}