Hacker News new | past | comments | ask | show | jobs | submit login

Read the error messages. Reread them. Extract every piece of information from them. Never close an error popup without having read and understood what it means. Always read stack traces when you're blessed enough to have them.

100% of the juniors (and an awful lot of seniors) I've ever trained simply ignore those and then come ask for help debugging.

99% of the time, the explanation for their problem is literally the first line of the stack trace/error message/whatever is red on their screen.

If you successfully adopt the mindset of "whatever the computer is telling is true from the computer point of view, so I'd better make sure I've read it" instead of "Anyway, I'll try something else and run again", you'll debug 10 times faster. And since debugging is a good part of the job, you'll be a lot more productive.




I really like your last point "whatever the computer is telling is true from the computer point of view".

In a surprising number of coding interviews that I've conducted, I've seen a candidate write an incorrect solution that generates an exception and then attempt to figure out the error almost on first principles, rather than actually reading the exception carefully and thinking about what coding errors could have caused it to happen. The exception message is a _huge_ hint!

It's a red flag to see someone try to debug without paying attention to it. Conversely it's very positive to see someone encounter one and then think carefully about it. Writing an incorrect solution but then testing it out and showing the ability to debug and fix it systematically and expediently is no worse than getting it right on the first try, in my book.


It isn't just a huge hint, most of the time it tells you exactly what the damn issue is. This discussion is blowing my mind.

I am honestly speechless. IT GIVES YOU THE LINE NUMBER AND THE FUNCTION CALL AND THE REASON! Why would you ever even attempt to debug without parsing it?


Wait wait wait.

Not so fast.

i'm one of those programmers who read manuals and stack traces but I have to admit that I've seen my share of both manuals as well as stack traces that didn't make much sense.


This is my experience with Clojure stack traces ~60-75% of the time. Perhaps it is a symptom of functional programming? A lot of function passing and anonymous functions make it difficult to generate a meaningful stack trace.


Clojure's stack traces are an issue with Clojure specifically, and how it's hosted on top of the JVM. A lot of the stack trace you get is actually implementation details of the Java objects making up the Clojure runtime. In effect, the program that you get stack traces of is the Clojure interpreter rather than your Clojure program which is running inside that interpreter.


Why do they do this, though? Shouldn't they have realized from the start that such messages are not helpful, and should have given messages related to your Clojure program? (Or is there some difficult technical issue related to achieving this?)

Asking because reading about this issue in the past is one thing that has kept me from trying out Clojure.


I've heard similar things about Scala, though I haven't had a chance to confirm this for myself yet. Not 100% sure if this supports your hypothesis or not given that Scala is multiparadigm?


Fortunately, I think 1.10 is going a long way towards fixing this.


My experience in Racket has been the opposite. Especially with the aid of DrRacket's stepping debugger, reading stack traces and finding bugs is rather painless. (Figuring out the best way to fix bugs, however, is no easier than in any other language.)


Functional programming not so much- languages like Elixir or JS used functionally don't have this problem generally.


Elixir has had difficult to parse error messages until relatively recently, but this has improved immensely (Dialyzer/Dialyxir in particular).


In what way? I've been using Elixir since late 2015 and have rarely had issues.


Yeah I agree. The way this thread is going you would think that all errors are easy and all you have to do is what the computer tells you and you’re done. I wish I’d known it was all that simple!


> stack traces that didn't make much sense.

Those can be especially prevalent in systems that utilize a lot of indirection.


Especially when you end up in C++ template hell.


That kind of stuff permanently damaged my ability to enjoy using C++.


Error in boost/bind.hpp:768


That's exactly what I felt as I was spending time on MSDN/Microsoft support website, reading about weird errors messages on Windows 95/98/NT/XP in 1990s. Some generic desc about the Windows error messages and no resolution.


as in tensorflow stack traces


And in the opposite direction: remember when you are writing error messages to include all the relevant details, and never ever write "this shouldn't happen" or "document later".


What about, log_panic("If we have somehow managed to hit this code path, this project if FUBAR. I would recommend giving up and starting over.")


Somehow reminds me of Aliens:

  I say we take off and nuke the entire site from orbit. It's the only way to be sure.
:-)


That's annoying but fine as long as it's unique and you can grep it in the codebase.


You can forgo something being unique if you just append line and file information to the log. Literally any logging framework (even homerolled ones) should give you this by default.


yup


If used correctly, «this should not happen» can be very useful, indicating e.g that the program is in some illegal state, as opposed to the input being incorrectly formatted.


Even if that’s the case, create a custom exception and embed in the custom exception object the actual exception.


Assert


That’s only true if the developer of the class module let the entire stack trace get through to the consumer.

In C# it’s the difference between...

try

{

....

}catch(Exception e)

{

//Do stuff

   throw new MyCustomException(“Something Bad happened”);
}

And

try

{

....

}catch(Exception e)

{

  //do stuff

   throw;

}


Erh, exception types should include an inner exception parameter, for the generic one they wrap. So neither of your two examples are all that great.


Why would I wrap an exception if I don’t plan to have special handling? If it is exception that I didn’t throw because of a business reason, why not just throw the original exception?


If you don't have any special handling, then why are you catching it at all?


That was the "//Do stuff" part, you may want to do some cleanup (in C# usually handled by a finally block or a "using" block), send a custom notification, logging (usually I would just let the client handle logging), etc.


In what case would you ever want the second example?


Always. If you can't resolve the exception and continue processing normally, you should never discard the exception you caught and create a new one of your own. If you must create one of your own, include the one you caught for the additional context.

It's pretty frustrating when you're debugging someone else's code and their error handling just throws away all of the useful information and replaces it with a generic "Something broke" kind of message.


usually you may catch a general exception and throw another one that's caught up the call stack. In this case i think it may be useful for logging additional causes that are not going to be obvious with just the stacktrace(?)


I remember once encountering an error that had all of the above plus it presented the solution to the issue right in the error message. It was amazing and why I still remember seeing that to this day.


Let's try that! I was trying to do some trivial task on a .gov website, but was greeted with the following, which I'm pasting here for your convenience. Could you spot the problem? Because their customer service can't.

  ---
Error Page Exception SRVE0260E: The server cannot use the error page specified for your application to handle the Original Exception printed below.

Original Exception: Error Message: javax.servlet.ServletException: /verification.xhtml at line 66 and column 34 action="#{verificationAction.authenticateClient()}": java.lang.NullPointerException Error Code: 500 Target Servlet: Faces Servlet Error Stack: javax.el.ELException: /verification.xhtml at line 66 and column 34 action="#{verificationAction.authenticateClient()}": java.lang.NullPointerException at org.apache.myfaces.view.facelets.el.TagMethodExpression.invoke(TagMethodExpression.java:95) at javax.faces.component._MethodExpressionToMethodBinding.invoke(_MethodExpressionToMethodBinding.java:88) at org.apache.myfaces.application.ActionListenerImpl.__AW_processAction(ActionListenerImpl.java:100) at org.apache.myfaces.application.ActionListenerImpl.processAction(ActionListenerImpl.java) at javax.faces.component.UICommand.broadcast(UICommand.java:120) at javax.faces.component.UIViewRoot._broadcastAll(UIViewRoot.java:995) at javax.faces.component.UIViewRoot.broadcastEvents(UIViewRoot.java:278) at javax.faces.component.UIViewRoot._process(UIViewRoot.java:1307) at javax.faces.component.UIViewRoot.processApplication(UIViewRoot.java:733) at org.apache.myfaces.lifecycle.InvokeApplicationExecutor.execute(InvokeApplicationExecutor.java:34) at org.apache.myfaces.lifecycle.LifecycleImpl.__AW_executePhase(LifecycleImpl.java:172) at org.apache.myfaces.lifecycle.LifecycleImpl.executePhase(LifecycleImpl.java) at org.apache.myfaces.lifecycle.LifecycleImpl.__AW_execute(LifecycleImpl.java:119) at org.apache.myfaces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java) at javax.faces.webapp.FacesServlet.__AW_service(FacesServlet.java:189) at javax.faces.webapp.FacesServlet.service(FacesServlet.java) at com.ibm.ws.webcontainer.servlet.ServletWrapper.service(ServletWrapper.java:1233) at com.ibm.ws.webcontainer.servlet.ServletWrapper.handleRequest(ServletWrapper.java:782) at com.ibm.ws.webcontainer.servlet.ServletWrapper.handleRequest(ServletWrapper.java:481) at com.ibm.ws.webcontainer.servlet.ServletWrapperImpl.handleRequest(ServletWrapperImpl.java:178) at com.ibm.ws.webcontainer.filter.WebAppFilterChain.invokeTarget(WebAppFilterChain.java:136) at com.ibm.ws.webcontainer.filter.WebAppFilterChain.doFilter(WebAppFilterChain.java:97) at com.icbc.licensing.filter.ThreadContextFilter.__AW_doFilter(ThreadContextFilter.java:92) at com.icbc.licensing.filter.ThreadContextFilter.doFilter(ThreadContextFilter.java) at com.ibm.ws.webcontainer.filter.FilterInstanceWrapper.doFilter(FilterInstanceWrapper.java:195) at com.ibm.ws.webcontainer.filter.WebAppFilterChain.doFilter(WebAppFilterChain.java:91) at org.apache.logging.log4j.web.Log4jServletFilter.__AW_doFilter(Log4jServletFilter.java:71) at org.apache.logging.log4j.web.Log4jServletFilter.doFilter(Log4jServletFilter.java) at com.ibm.ws.webcontainer.filter.FilterInstanceWrapper.doFilter(FilterInstanceWrapper.java:195) at com.ibm.ws.webcontainer.filter.WebAppFilterChain.doFilter(WebAppFilterChain.java:91) at com.ibm.ws.webcontainer.filter.WebAppFilterManager.doFilter(WebAppFilterManager.java:967) at com.ibm.ws.webcontainer.filter.WebAppFilterManager.invokeFilters(WebAppFilterManager.java:1107) at com.ibm.ws.webcontainer.servlet.CacheServletWrapper.handleRequest(CacheServletWrapper.java:87) at com.ibm.ws.webcontainer.WebContainer.handleRequest(WebContainer.java:949) at com.ibm.ws.webcontainer.WSWebContainer.handleRequest(WSWebContainer.java:1817) at com.ibm.ws.webcontainer.channel.WCChannelLink.ready(WCChannelLink.java:200) at com.ibm.ws.http.channel.inbound.impl.HttpInboundLink.handleDiscrimination(HttpInboundLink.java:463) at com.ibm.ws.http.channel.inbound.impl.HttpInboundLink.handleNewRequest(HttpInboundLink.java:530) at com.ibm.ws.http.channel.inbound.impl.HttpInboundLink.processRequest(HttpInboundLink.java:316) at com.ibm.ws.http.channel.inbound.impl.HttpInboundLink.ready(HttpInboundLink.java:287) at com.ibm.ws.tcp.channel.impl.NewConnectionInitialReadCallback.sendToDiscriminators(NewConnectionInitialReadCallback.java:214) at com.ibm.ws.tcp.channel.impl.NewConnectionInitialReadCallback.complete(NewConnectionInitialReadCallback.java:113) at com.ibm.ws.tcp.channel.impl.AioReadCompletionListener.futureCompleted(AioReadCompletionListener.java:175) at com.ibm.io.async.AbstractAsyncFuture.invokeCallback(AbstractAsyncFuture.java:217) at com.ibm.io.async.AsyncChannelFuture$1.__AW_run(AsyncChannelFuture.java:205) at com.ibm.io.async.AsyncChannelFuture$1.run(AsyncChannelFuture.java) at com.ibm.ws.util.ThreadPool$Worker.run(ThreadPool.java:1892) Caused by: java.lang.NullPointerException at com.icbc.licensing.clio.action.VerificationAction.DbAuthInsertandCount(VerificationAction.java:118) at com.icbc.licensing.clio.action.VerificationAction.__AW_authenticateClient(VerificationAction.java:161) at com.icbc.licensing.clio.action.VerificationAction.authenticateClient(VerificationAction.java) at sun.reflect.GeneratedMethodAccessor440.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:56) at java.lang.reflect.Method.invoke(Method.java:620) at org.apache.el.parser.AstValue.__AW_invoke(AstValue.java:268) at org.apache.el.parser.AstValue.invoke(AstValue.java) at org.apache.el.MethodExpressionImpl.invoke(MethodExpressionImpl.java:278) at org.apache.myfaces.view.facelets.el.TagMethodExpression.invoke(TagMethodExpression.java:83) ... 46 more

Error Page Exception: Error Message: javax.servlet.ServletException: WebBeans context with scope type annotation @RequestScoped does not exist within current thread Error Code: 0 Target Servlet: Error Stack: javax.enterprise.context.ContextNotActiveException: WebBeans context with scope type annotation @RequestScoped does not exist within current thread at org.apache.webbeans.container.BeanManagerImpl.__AW_getContext(BeanManagerImpl.java:363) at org.apache.webbeans.container.BeanManagerImpl.getContext(BeanManagerImpl.java) at org.apache.webbeans.intercept.NormalScopedBeanInterceptorHandler.getContextualInstance(NormalScopedBeanInterceptorHandler.java:125) at org.apache.webbeans.intercept.NormalScopedBeanInterceptorHandler.invoke(NormalScopedBeanInterceptorHandler.java:96) at org.apache.webbeans.conversation.ConversationImpl_$$_javassist_0.isTransient(ConversationImpl_$$_javassist_0.java) at ...


verifcationAction.authenticateClient() call hit a null ref exception (called from the template at /verification.xhtml) and their error page is trying to use a webBean context which hasn't been setup or activated yet to display the error.

The original site of the null ref error seems to be the DBAuthInsert method at com.icbc.licensing.clio.action.VerificationAction.DbAuthInsertandCount(VerificationAction.java:118) so maybe a getConnection is returning a null and not being checked, or some config variable is borked. Would start digging at line 118 to work out what variables are being dereferenced and where their values come from.



The server process running in a thread other than the router? Dunno, I’m sure I could figure it out having access to the code. Support most likely does not employ software engineers / SREs.


In my experience, the progression of skill comes almost full circle. Juniors don't read error messages and try to debug "from first principles". Seniors pay attention to error messages. Experts often don't (initially) read the error message, because the very fact that there is an error immediately clues them in on its likely cause.


I had a candidate once. They were having trouble getting the message body on a 400 level error during the technical interview. They had chosen to solve it in Java. I'm not a Java developer (and I let the candidate know ahead of time and that I could only help with naive suggestions). Their exception message was pretty clear. After they prodded around for a bit getting nowhere, I pointed out the exception message. He ignored my suggestions multiple times (multiple angles of "your exception says blah"). After the interview, I took his solution and fixed it per the exception message. Boom. Worked. After the interview, his feedback to our recruiter was that it was obvious I did not know Java. Some people are bad at reading and listening :(. Also, this was a former Googler. It helped shatter an (unconscious?) bias that somehow Googlers were a breed above the rest.


In principle, I couldn't agree more. I get this from time to time, someone is stumped on an exception and after seeing it, I usually go "well, it's a null pointer exception in this method, so from looking at the code, it could come from these 3 places".

The pattern I see there is that a) in release builds, exceptions on many platforms suck. Things get inlined, parts of the stack trace are missing etc. If you understand why that is, that's not so much of a problem. But: b) people often have this Voodoo-like mentality about this. They often see the 100th stack trace that doesn't make much sense and try to wing it, because that's obviously always the case. The best thing you can do look at them together: Lead by example, think aloud and you might just teach someone how to read it in a better way.

It's rather important you have a consistent and accurate mental model of why your stack traces are mangled, lest you end up like one of those beasts that got random treats in B.F. Skinner's experiments and ended up with the strangest behaviors possible :-)


People never read error messages. Most of them click straight through 8 milliseconds after the error appears, without taking the slightest opportunity to understand.

It's rage-inducing when troubleshooting over laggy screensharing sessions. If enough frames drop, I may not even see that the error was raised.


Related: Read the official documentation. I can’t count how many times junior folks have asked me things like, does FooBarFactory constructor take an array of ints or floats? Well, what does their documentation say? Sometimes the official documentation is crap and that’s a reasonable question, but usually the answers can be found in TFM.


I noticed a similar trend in JS dev: asking/looking for (video) tutorials instead of reading the official doc. Even when the doc is good and quick too read (React, Redux, Vue, etc).


Related to that, usually I am a little faster finding answers to syntax questions on SO than in the documentation, even when the documentation is good. So I'm glad that people have been asking those basic questions on SO.


Yeah, it's a bit weird why people have this expectation to reach for tutorials by random person X instead of looking for the official docs. I can't count the number of time someone asked me which tutorials to read to learn Go, but hadn't even considered reading the docs.


I tend to grok things a great


I work in QA and I wish more developers thought this way. Also run your code before hand off. Please. If I had a nickel for every time I got a user story or bug to validate and it immediately crashes then I would have a bunch of nickels. Which wouldn't be super helpful, but still. Please at least run your code, or ideally you should have CI/CD with unit tests, but we all know that isn't always the case especially in legacy Enterprise software.


It's easy to make the assumption that developers haven't ran the code at all, but I find in reality that's rarely the case. In my experience it almost always has something to do with an unexpected environment variant.

It's very easy as a developer to get so comfortable with the "happy path" of navigating code that you forget to look outside of the happy path when devising your test cases. It takes time to learn to identify the test cases you're not regularly exposing yourself, and these test cases are often ones not described in a requirements or specs doc.


This! I spent the first half of my career in QA. This second half in dev. Coming from QA I'm very meticulous about everything and yet as soon as it leaves my hands something comes up. This is why I'm a big proponent of QA. I had a chat with someone who was pretty much dismissing QA because they had the mindset of I'll just test it or my cofounder will help. It's terribly surprising what goes down outside the confines.


As a QA dev, myself, I run across this occasionally. For a recent bug, the dev couldn't reproduce it, yet I could consistently. The difference: the width the window we were using; he was using the default, I was not. Most of the bugs I run into are not config dependent, so it is always surprising when one is.


As a developer this bugs me too. I once was team lead on a team which had two developers (bad hires) who seemed to believe that it was only their job to write the code, someone else's job to run it and test it. I started putting "and test the code" in the tasks; a team member successfully lobbied our manager that that was another task. So now I had to write 2x as many tasks, and the predictable result happened: the developers who didn't test their own code grabbed all the "write the feature" tasks and banged them out rapidly (easy to do when you don't make sure it works!) The more experienced developers were left to pick up the "and test what you wrote" tasks, which really amounted to rewriting the bad code. Then management said, "Why are you (senior developers) wasting so much time testing code? It generally seems to work and we can always fix it later."

There was just no way to win. I'm glad I don't work in that environment anymore.


Ok, I'm strictly a hobby developer so I don't really have co-workers to go ask for help. Do people really not read error messages? That boggles my mind. How would you ever figure out what went wrong?

I mean, how could anyone ever learn to program well enough to get a job as a professional programmer without knowing to do that?


A lot of programming jobs just need someone who can follow directions, not someone very skilled. It's the digital equivalent putting together IKEA furniture instead of being the person doing all the design work to ensure things fit together and the directions are simple to follow.

The most common way I've seen people try to figure out what went wrong is sprinkling stuff like "console.log("Here1"), console.log("Here2") throughout the code until they figure out which "Here" statement wasn't printed, then they start making random changes to values until the error reflects the changes that they made, then they check stackoverflow, if there aren't any results, they'll paste the error message with no context, having learned nothing in the process. It's pretty common in any field where you can get by knowing "what" to do, vs "why" to do something. I've seen the same thing teaching Linux classes where someone throws up their hands and gives up because they tried to cat a file and it said "error: no such file or directory", but they just saw "error", and decided it was too difficult.


There are certain people that just don't have developed critical thinking skills and will shut down the minute something goes different than expected. I have dealt with a few of those types of people in my career and it is incredibly frustrating. The best thing you can do for them is not to help them in the way they want, by giving them the answer, but by making them do some of the grunt work on their own before they even come to you. I ask a lot of questions about what they are seeing and if they can't answer them then I make them go back until they can. Simple things like "what have you searched for regarding this", "what does the error message say" in order to get them in the habit of actually starting to look into their own issues.


The obvious answer is they don't figure out what went wrong, they tweak the program until the problem goes away for a while.


100% agree.

Likewise, I think adopting a "how can I create a stack trace/error message" mindset is incredibly important.

Can you add a breakpoint to a certain piece of code? Could you add a try/catch statement somewhere to catch the error?

Far too often good engineers do not have an "active" mindset in hunting for stack traces/error messages, instead, they wait for them to fall like manna from heaven.


Raise your hand if you've ever created an insert/update database trigger that explodes when that it sees that one bad value you've been investigating, just so you'll have a traceback that points to the offending code.

(Hopefully only in your development environment.)


I think a lot of the time people are perfectly capable of figuring out the issue on their own, however they ask for help for social reasons. Getting another person involved in the problem is way more fun, and creates a bond between both people.


When you’re messaging me 20 times a day about simple exceptions like “X gem is not installed” (Ruby) and asking me what to do, then it has an opposite offect of creating a bond


For non black and white problems it makes more sense though. Discussing the best approach for something complex, for example.

Timing is important though, you don't want to intrude, interrupt or distract.


I've observed this a few times, too.


This is a good insight. Many a time I've offered a fix to a bug and the response was "how did you figure that out?"

My answer was that I looked at the error message and then, if it was available, scanned the backtrace to figure out which files/method calls were involved. Otherwise (particularly with JS), I set up some breakpoints in the browser to help visualise things better.

I've come across plenty of senior developers who ignore what the machine says. The thing is, most bugs aren't solved through intuition, so approaching the problem as if your gut feeling is completely accurate is going to turn a 10 minute debugging session into something that could last a full day. As, in my experience, it once did.


In general, I agree that juniors tend to skip useful information, but it depends a bit on the technology you are working with. PHP, for example, was known to report the error for the line after the one you forgot the semicolon at or at the line after the last line of the file when a parenthesis was missing somewhere.

While there is a reason why it did so, those are error messages which are quite hard to understand the first few times they occur to you.

Nevertheless, those lousy error message should be exceptions nowadays, and if you are using decent technologies, the chances are much higher, that you just didn't read the error message not careful enough to understand the problem.


My comment is a bit extreme in its tone indeed !

It's just that it's the advice I've given the most by orders of magnitudes, and I'm still amazed (and a bit weary) it's worth even mentioning. And to make it stick, I've removed any nuance of it...


Eventually you'll learn what errors can be ignored, what are useless information, and which ones are sending you down a path that will waste time. But only start skipping and skimming once you've learned what to look for, and always be prepared to toss out the old knowledge when working on a new system.

Also, when you have far too many logs, reading in reverse chronological order works most of the time, but not all the time. Generally the last error you are looking at is the error you want to find and fix, but always consider that sometimes an earlier error that seems unrelated can be the reason a later error is even possible.


This is excellent advice, and I would suggest reading warnings (e.g., compiler warnings) as well – even if they don't indicate a problem, understanding why a warning is thrown is often a very worthwhile exercise.


An addendum to that: Design your error messages. When dealing with exceptions, catch/wrap the exception (e.g. using InnerException in .NET) and add important information if necessary.

You'll be glad you did six months down the line, when the exception actually pops up and you instantly know how to reproduce the bug (perhaps because you know the id of the business object involved).

Edit: Also, use understandable text for error messages. Don't go around talking about threads/sockets etc. unless they happen on that abstraction level. The user should see business-level errors, so they know what happened in the business domain and why ("we don't know why, this shouldn't have happened" is a reasonable answer there, too).

Edit 2: I just thought of Git's "plumbing vs porcelain" metaphor. Abusing it slightly: It's much more pleasant to be notified of an error by a red light instead of standing ankle-deep in sewage. I guess that metaphor breaks down a bit, since toilets more often have the latter than the first :)


I agree that this is one of the most useful things that you can do and one of the most commonly overlooked for whatever reason. I can't underestimate the number of times I've worked with someone who is looking in seemingly random places to solve an error instead of tracing backwards from the error message in front of their face.


Definitely this. Reading logs is a skill.

It's pretty much the same for debugging. I notice the juniors often start to look at where the problem may be. I usually try to first look where it isn't, so I can drill down to where it actually it, in stead of just looking at code and hoping to find what's wrong.


Totally concur, that's very similar to the method I explain to folks when they ask how I go about debugging.

I've always visualized it in my head kind of like a binary search tree, or maybe the bisection method in math for finding roots. I want to think about as little code as possible, so I take steps to cut the problem space down as dramatically and quickly as possible.

Plus, the less I have to think about when debugging the less likely I am to end up with an actual headache.



Also great advice for a senior. Great advice for anyone really.




Guidelines | FAQ | Support | API | Security | Lists | Bookmarklet | Legal | Apply to YC | Contact

Search: