RuntimeError is a descendant of StandardError, and rescues happen exactly once per exception handling block. That is, your first code example will never hit RuntimeError because it's swallowed by the implicit StandardError. Here's a class tree of Exception from my local irb: - Exception |- ErrorHighlight::Spotter::NonAscii |- IRB::Abort |- IRB::LoadAbort |- NoMemoryError |- ScriptError | |- LoadError | | `- Gem::LoadError | | |- Gem::ConflictError | | `- Gem::MissingSpecError | | `- Gem::MissingSpecVersionError | |- NotImplementedError | `- SyntaxError |- SecurityError |- SignalException | `- Interrupt |- StandardError | |- ArgumentError | | |- Gem::Requirement::BadRequirementError | | |- IO::Buffer::MaskError | | `- UncaughtThrowError | |- EncodingError | | |- Encoding::CompatibilityError | | |- Encoding::ConverterNotFoundError | | |- Encoding::InvalidByteSequenceError | | `- Encoding::UndefinedConversionError | |- FiberError | |- Fiddle::Error | | |- Fiddle::ClearedReferenceError | | `- Fiddle::DLError | |- Gem::Molinillo::ResolverError | | |- Gem::Molinillo::CircularDependencyError | | |- Gem::Molinillo::NoSuchDependencyError | | `- Gem::Molinillo::VersionConflict | |- Gem::TSort::Cyclic | |- IOError | | |- EOFError | | `- IO::TimeoutError | |- IRB::CantChangeBinding | |- IRB::CantReturnToNormalMode | |- IRB::Command::CommandArgumentError | |- IRB::IllegalParameter | |- IRB::IrbAlreadyDead | |- IRB::IrbSwitchedToCurrentThread | |- IRB::NoSuchJob | |- IRB::RubyLex::TerminateLineInput | |- IRB::SourceFinder::EvaluationError | |- IRB::UndefinedPromptMode | |- IRB::UnrecognizedSwitch | |- IndexError | | |- KeyError | | `- StopIteration | | |- ClosedQueueError | | `- Ractor::ClosedError | |- JSON::JSONError | | |- JSON::GeneratorError | | |- JSON::MissingUnicodeSupport | | `- JSON::ParserError | | `- JSON::NestingError | | `- JSON::CircularDatastructure | |- LocalJumpError | |- Math::DomainError | |- NameError | | `- NoMethodError | |- NoMatchingPatternError | | `- NoMatchingPatternKeyError | |- RangeError | | `- FloatDomainError | |- RegexpError | | `- Regexp::TimeoutError | |- Reline::ConfigEncodingConversionError | |- Reline::Terminfo::TerminfoError | |- Ripper::TokenPattern::Error | | |- Ripper::TokenPattern::CompileError | | `- Ripper::TokenPattern::MatchError | |- RuntimeError | | |- FrozenError | | |- Gem::Exception | | | |- Gem::CommandLineError | | | |- Gem::DependencyError | | | | |- Gem::DependencyResolutionError | | | | `- Gem::UnsatisfiableDependencyError | | | |- Gem::DependencyRemovalException | | | |- Gem::DocumentError | | | |- Gem::EndOfYAMLException | | | |- Gem::FilePermissionError | | | |- Gem::FormatException | | | |- Gem::GemNotFoundException | | | | `- Gem::SpecificGemNotFoundException | | | |- Gem::GemNotInHomeException | | | |- Gem::ImpossibleDependenciesError | | | |- Gem::InstallError | | | | `- Gem::RuntimeRequirementNotMetError | | | |- Gem::InvalidSpecificationException | | | |- Gem::OperationNotSupportedError | | | |- Gem::RemoteError | | | |- Gem::RemoteInstallationCancelled | | | |- Gem::RemoteInstallationSkipped | | | |- Gem::RemoteSourceException | | | |- Gem::RequestSet::Lockfile::ParseError | | | |- Gem::RubyVersionMismatch | | | |- Gem::UninstallError | | | |- Gem::UnknownCommandError | | | |- Gem::VerificationError | | | `- Gem::WebauthnVerificationError | | |- IO::Buffer::AccessError | | |- IO::Buffer::AllocationError | | |- IO::Buffer::InvalidatedError | | |- IO::Buffer::LockedError | | |- RDoc::Error | | |- Ractor::Error | | | |- Ractor::IsolationError | | | |- Ractor::MovedError | | | |- Ractor::RemoteError | | | `- Ractor::UnsafeError | | `- Reline::Config::InvalidInputrc | |- SystemCallError | | |- Errno::E2BIG | | |- Errno::EACCES | | |- Errno::EADDRINUSE | | |- Errno::EADDRNOTAVAIL | | |- Errno::EAFNOSUPPORT | | |- Errno::EAGAIN | | | |- IO::EAGAINWaitReadable | | | `- IO::EAGAINWaitWritable | | |- Errno::EALREADY | | |- Errno::EAUTH | | |- Errno::EBADARCH | | |- Errno::EBADEXEC | | |- Errno::EBADF | | |- Errno::EBADMACHO | | |- Errno::EBADMSG | | |- Errno::EBADRPC | | |- Errno::EBUSY | | |- Errno::ECANCELED | | |- Errno::ECHILD | | |- Errno::ECONNABORTED | | |- Errno::ECONNREFUSED | | |- Errno::ECONNRESET | | |- Errno::EDEADLK | | |- Errno::EDESTADDRREQ | | |- Errno::EDEVERR | | |- Errno::EDOM | | |- Errno::EDQUOT | | |- Errno::EEXIST | | |- Errno::EFAULT | | |- Errno::EFBIG | | |- Errno::EFTYPE | | |- Errno::EHOSTDOWN | | |- Errno::EHOSTUNREACH | | |- Errno::EIDRM | | |- Errno::EILSEQ | | |- Errno::EINPROGRESS | | | |- IO::EINPROGRESSWaitReadable | | | `- IO::EINPROGRESSWaitWritable | | |- Errno::EINTR | | |- Errno::EINVAL | | |- Errno::EIO | | |- Errno::EISCONN | | |- Errno::EISDIR | | |- Errno::ELAST | | |- Errno::ELOOP | | |- Errno::EMFILE | | |- Errno::EMLINK | | |- Errno::EMSGSIZE | | |- Errno::EMULTIHOP | | |- Errno::ENAMETOOLONG | | |- Errno::ENEEDAUTH | | |- Errno::ENETDOWN | | |- Errno::ENETRESET | | |- Errno::ENETUNREACH | | |- Errno::ENFILE | | |- Errno::ENOATTR | | |- Errno::ENOBUFS | | |- Errno::ENODATA | | |- Errno::ENODEV | | |- Errno::ENOENT | | |- Errno::ENOEXEC | | |- Errno::ENOLCK | | |- Errno::ENOLINK | | |- Errno::ENOMEM | | |- Errno::ENOMSG | | |- Errno::ENOPOLICY | | |- Errno::ENOPROTOOPT | | |- Errno::ENOSPC | | |- Errno::ENOSR | | |- Errno::ENOSTR | | |- Errno::ENOSYS | | |- Errno::ENOTBLK | | |- Errno::ENOTCONN | | |- Errno::ENOTDIR | | |- Errno::ENOTEMPTY | | |- Errno::ENOTRECOVERABLE | | |- Errno::ENOTSOCK | | |- Errno::ENOTSUP | | |- Errno::ENOTTY | | |- Errno::ENXIO | | |- Errno::EOPNOTSUPP | | |- Errno::EOVERFLOW | | |- Errno::EOWNERDEAD | | |- Errno::EPERM | | |- Errno::EPFNOSUPPORT | | |- Errno::EPIPE | | |- Errno::EPROCLIM | | |- Errno::EPROCUNAVAIL | | |- Errno::EPROGMISMATCH | | |- Errno::EPROGUNAVAIL | | |- Errno::EPROTO | | |- Errno::EPROTONOSUPPORT | | |- Errno::EPROTOTYPE | | |- Errno::EPWROFF | | |- Errno::ERANGE | | |- Errno::EREMOTE | | |- Errno::EROFS | | |- Errno::ERPCMISMATCH | | |- Errno::ESHLIBVERS | | |- Errno::ESHUTDOWN | | |- Errno::ESOCKTNOSUPPORT | | |- Errno::ESPIPE | | |- Errno::ESRCH | | |- Errno::ESTALE | | |- Errno::ETIME | | |- Errno::ETIMEDOUT | | |- Errno::ETOOMANYREFS | | |- Errno::ETXTBSY | | |- Errno::EUSERS | | |- Errno::EXDEV | | `- Errno::NOERROR | |- ThreadError | |- TypeError | `- ZeroDivisionError |- SystemExit | `- Gem::SystemExitException |- SystemStackError `- fatal -a On Tue, Oct 29, 2024 at 8:48 AM Rodrigo Rosenfeld Rosas via ruby-core < ruby-core@ml.ruby-lang.org> wrote: > Hello, I couldn't find any documentation about the subject, so I thought > this behavior should be probably documented. > > Given the following code: > > def raise_error > raise "runtime error message" > rescue => e > "StandardError: #{e.message}" > rescue RuntimeError => e > puts "RuntimeError raised: #{e.message}" > raise StandardError, "standard error message" > end > > # same, but the order of the rescue blocks are inverted > def raise_error2 > raise "runtime error message" > rescue RuntimeError => e > puts "RuntimeError raised: #{e.message}" > raise StandardError, "standard error message" > rescue => e > "StandardError: #{e.message}" > end > > p ["raise_error", raise_error ] > > begin > p ["raise_error2", raise_error2] > rescue => e > puts "raise_error2 raised: #{e.message}" > end > > When we run it, this is the output in Ruby 3.3.5: > > ["raise_error", "StandardError: runtime error message"] > > RuntimeError raised: runtime error message > > raise_error2 raised: standard error message > > > In the first case (raise_error), the code raised from the RuntimeError > rescue block is rescued by the StandardError block, but when inverting the > order of the rescue blocks (raise_error2) then this won't happen. > > Is this part of the specs? Is this behavior documented somewhere? Could > this behavior differ in different Ruby implementations and versions? Or can > we rely on such behavior? > > > This chapter doesn't include such case in its examples: > > https://ruby-doc.com/docs/ProgrammingRuby/html/tut_exceptions.html > > Or this documentation about Exceptions: > > https://ruby-doc.org/3.3.5/syntax/exceptions_rdoc.html > > Is there a recommended way for wrapping exceptions into a particular one > and then handling that exception from within the same method rescue blocks? > Or is this considered a bad practice? > > > ______________________________________________ > ruby-core mailing list -- ruby-core@ml.ruby-lang.org > To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org > ruby-core info -- > https://ml.ruby-lang.org/mailman3/lists/ruby-core.ml.ruby-lang.org/ -- Austin Ziegler • halostatue@gmail.com • austin@halostatue.ca http://www.halostatue.ca/ • http://twitter.com/halostatue