Zum Hauptinhalt springen
Echtzeit-Radar & Feeds
Alle RSS Feeds ➔
👥 Community & Social
Windows Tipps & SecurityGrafikkarte vor Überhitzung schützen: So geht’s(25.09.2026 um 08:00 Uhr)
••••••••••
Windows Tipps & SecurityGrafikkarte vor Überhitzung schützen: So geht’s(25.09.2026 um 08:00 Uhr)
••••••••••
Intelligence View
⚡ tsecurity.de Intelligence

The Silent Killers in Your Go App: Unhandled Errors

Do you know what's scarier than a panic in a Golang application? 🤔 An error that doesn't panic, doesn't log, doesn't show up - but silently fails in production. These are like landmines scattered across your codebase, patiently waiting to b…

0
↗ Quelle (dev.to)
Reagiere als Erste:r — dein Feedback zählt!

Do you know what's scarier than a panic in a Golang application? 🤔

An error that doesn't panic, doesn't log, doesn't show up - but silently fails in production.

These are like landmines scattered across your codebase, patiently waiting to blow up at the worst possible time - like when you're sipping on a cold beer 🍺, celebrating a successful release.

And then…




📞 "Hey, the feature isn't working. Logs look clean. What's going on?"




Oh no. That's your PM. On a weekend. With no error trace.

I've been there. And yes, I left my beer mid-sip. 😞



When I first started with Go, I didn't fully grasp that errors in Go are first-class citizens - passed around just like any other value. If you don't handle them, they don't scream. They whisper. And that whisper can turn into a production outage.



So this article is not just a rant. It's a survival guide - to help you prevent these ghost errors from ruining your weekend or your system's reputation. 🧯



Let's walk through:




  • How to structure your error objects for better debugging

  • How to distinguish between expected and unexpected errors

  • And how to turn unhandled errors into actionable alerts for your team





Why Error Handling in Go Needs Your Full Attention



Not all errors are created equal. Some are edge cases you anticipate. Some are wild cards. But all of them need structure and context if you want to trace them later.



✅ Expected (or edge-case) errors:




  • User requests a resource that doesn't exist

  • Invalid query parameter

  • Authorization failure

  • Timeout or network delay



These are normal. They happen. You handle them, maybe return a 400 or 403, and move on.



❌ Unexpected errors (the real troublemakers):




  • DB connection fails

  • Memory exhaustion

  • Filesystem full

  • Internal services misbehaving

  • Panic due to nil pointer dereference



These should never happen - but if they do, you want to know immediately.





What Should Your Error Object Include?



Think of your error like a black box recorder. When things go wrong, it should tell you exactly what happened and how to reproduce it. Here's what it must include:





🧩 What happened?



A clear description: e.g. "invalid DB credentials", "failed to marshal JSON", or "timeout while calling payment gateway".





📍 Where did it happen?



Add stack traces or function names - Go makes this easy with tools like runtime.Caller() or libraries like pkg/errorsor xerrors.





⏱️ When did it happen?



Include request context: user ID, request ID, trace ID, timestamp - all of these help recreate the scenario and debug faster.





💬 What should the user see?



Don't leak internals! Instead, return friendly, actionable messages:




"Something went wrong. Please get in touch with support with Reference ID: xyz123."






🔍 How can the devs debug it further?



Include traceable metadata in logs or bug tracking systems so your team has breadcrumbs to follow. Reference IDS go a long way.



In the next section, we'll build a real REST API example and demonstrate how to put this into practice, gracefully handling, categorising, and propagating errors up to monitoring and alerting systems.



But before that, just remember this:

Handling errors isn't just about avoiding panics - it's about staying in control of your system even when it misbehaves.





Building a REST API in Go (Without Losing Your Mind Over Errors)



So, imagine we're building a REST API server - nothing fancy, just your good old layered Go backend:





  • Router Layer: Parses the HTTP request and routes it appropriately.


  • Service Layer: Decides whether to hit the DB, cache, or file system.


  • Persistence Layer: Deals directly with the database.



Let's zoom into the persistence layer, where two common issues can ruin your day:




  1. The database isn't reachable (classic).

  2. The data just… doesn't exist (also classic).



For the second case, we create a custom error structure to wrap all the "not found" drama:




type ObjectNotFoundError struct {
ObjectType string `json:"objectType"`
ObjectIdentifiers map[string]any `json:"objectIdentifier"`
StackTrace string `json:"stackTrace"`
}

func (e *ObjectNotFoundError) Error() string {
return fmt.Sprintf("%s not found, filter properties: [%v]", e.ObjectType, e.ObjectIdentifiers)
}
func (e *ObjectNotFoundError) LogMessage() string {
b, _ := json.Marshal(e)
return string(b)
}









🧠 What's Cool About This?





  • Error() gives a user-friendly summary.


  • LogMessage() gives devs all the gritty details.


  • ObjectType tells us what was missing.


  • ObjectIdentifiers explains how we looked for it.


  • StackTrace points us right to where the problem happened.



Now, let's see this in action in the persistence layer:




func (usrPer *UserPersistence) Get(ctx context.Context, id int) (*entities.User, error) {
sqlQuery := fmt.Sprintf("select * from users where id = %d", id)
data, err := usrPer.baseDb.Query(ctx, sqlQuery)
if err != nil {
return nil, err // --- 1️⃣
}
if id <= 0 {
return nil, &ObjectNotFoundError{. // --- 2️⃣
ObjectType: "User",
ObjectIdentifiers: map[string]any{"id": id},
StackTrace: string(debug.Stack()),
}
}
fmt.Println(data)
// else we will extract data from the data, for this we will hard code this return data
return &entities.User{
Id: id,
Name: fmt.Sprintf("user-%d", id),
CreatedDate: time.Now().Add(-10 * time.Hour),
}, nil
}









Breakdown




  • 1️⃣ We forward the DB error if it happens.

  • 2️⃣ We simulate an invalid user ID scenario (I know validation doesn't belong here, but bear with me - this is just for the demo).



A small disclaimer: I know this is not the right place for this validation, but I have only done this to explain how to gracefully propagate the error from the innermost layer.






Now Let's Head Over to the Service Layer 🚪



Here's where we catch and convert those deep, scary errors into something the client can actually digest.




type ServiceError struct {
InnerError error
HttpStatusCode int
HttpResponse *dto.ErrorResponse
}

func (e *ServiceError) Error() string {
return e.InnerError.Error()
}

func (e *ServiceError) GetHttpStatusCode() int {
return e.HttpStatusCode
}

func (e *ServiceError) GetErrorResponse() *dto.ErrorResponse {
return e.HttpResponse
}






We also define a handy interface:




type ILogMessageError interface {
error
LogMessage() string
}






Now, here's the service logic in action:




func (svc UserService) Get(ctx context.Context, id int) (*dto.User, error) {
fmt.Println("get in the svc")
userInfo, err := svc.userPersistenceObj.Get(ctx, id)
if err != nil {
if e, ok := err.(ILogMessageError); ok {
return nil, svc.WrapError(e)
}
logBug(err)
return nil, fmt.Errorf("can not retrive user: %d", id)
}
return (&dto.User{}).Init(userInfo), nil
}









🧪 What's Happening Here?





  • Interface Check: We see if the error implements ILogMessageError - if yes, it's a known error.


  • WrapError: Transforms the error into a format with HTTP status and message.


  • logBug(): If it's an unexpected error, alert the engineering team. PagerDuty bells ring. People panic. You know the drill.




// handle all types of errors that we expect the persistence to return
// or we can have a builder to build the ServiceError, and also follow
// The open and closed principle, and single responsibility principal
func (svc UserService) WrapError(err ILogMessageError) *ServiceError {
var objNotFound *persistence.ObjectNotFoundError
var result *ServiceError
if errors.As(err, &objNotFound) {
result = &ServiceError{
InnerError: err,
HttpStatusCode: http.StatusNotFound,
HttpResponse: &dto.ErrorResponse{
ErrorCode: "usr-404",
ErrorDescription: err.Error(),
},
}
}
return result
}
func logBug(err error) {
// log a bug and page enginnering team about the error
log.Println("logged a bug for error", err, string(debug.Stack()))
}









Finally, The Router - The Gatekeeper to the Client 🌐



Here, we check whether the service-level error can be converted to an HTTP response.




type IHttpResponseError interface {
GetHttpStatusCode() int
GetErrorResponse() *dto.ErrorResponse
}

func (r UserRouter) Get(c *gin.Context) {
var userReq dto.User
if err := c.ShouldBindUri(&userReq); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
userInfo, err := r.userServiceObj.Get(c.Request.Context(), userReq.Id)
if err != nil {
if e, ok := err.(IHttpResponseError); ok {
c.JSON(e.GetHttpStatusCode(), e.GetErrorResponse())
return
}
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, userInfo)
}









🧩 Why This Works





  • Handled errors bubble up in a predictable format with HTTP codes.


  • Unhandled errors raise the flag to devs and get returned as generic 500s.

  • Clients stay informed. Devs stay sane. Production stays alive. 😅






🎯 Final Thoughts



And there you have it - an end-to-end error handling strategy that:




  • Keeps your layers clean and decoupled

  • Makes debugging a breeze with structured error logs

  • Ensures your users never see a dreaded "500 Internal Server Error" without context






🔗 Full source code available on GitHub:



👉 architagr/The-Weekly-Golang-Journal - error-propagation






Stay Connected!



💡 Follow me on LinkedIn: Archit Agarwal 

🎥 Subscribe to my YouTube: The Exception Handler

 📬 Sign up for my newsletter: The Weekly Golang Journal

✍️ Follow me on Medium: @architagr

👨‍💻 Join my subreddit: r/GolangJournal

1. Sofort-Triage & Abwehrmaßnahmen

SOC Incident Playbook: Remote Code Execution (RCE) Defense
Syntax validiert (0 Fehler)
title: Detect Exploitation - The Silent Killers in Your Go App: Unhandled Errors
id: 4be40b47-4d75-4bf4-b1c7-681843d7498a
status: experimental
description: Automatisch generierte SIEM-Erkennungsregel basierend auf CTI Intelligence
references:
  - https://tsecurity.de/
author: iShareStuff CTI Automated Detection Engine
date: 2026-09-25
logsource:
  category: network_connection
  product: any
detection:
  selection:
      CommandLine|contains:
        - 'exploit'
  condition: selection
falsepositives:
  - Legitime administrative Zugriffe oder Penetrationstests
level: high
tags:
  - attack.initial_access
Syntax validiert (0 Fehler)
rule CTI_Threat_Indicator {
    meta:
        author = "iShareStuff CTI Automated Detection Engine"
        date = "2026-09-25"
        description = "YARA Signature for "
    strings:
        $str = "The Silent Killers in Your Go " ascii wide
    condition:
        any of them
}
Syntax validiert (0 Fehler)
index=security sourcetype IN ("cisco:asa", "pan:traffic", "zeek_conn", "suricata", "WinEventLog:Security")
("The Silent Killers in Your Go App Unhand")
| stats count earliest(_time) as first_seen latest(_time) as last_seen by src_ip, dest_ip, dest_host, signature
| eval first_seen=strftime(first_seen, "%Y-%m-%d %H:%M:%S"), last_seen=strftime(last_seen, "%Y-%m-%d %H:%M:%S")
| sort - count
Syntax validiert (0 Fehler)
message: "*The Silent Killers in Your Go App Unhand*"
Syntax validiert (0 Fehler)
CommonSecurityLog
| where Message has "The Silent Killers in Your Go App Unhand"
| summarize EventCount = count(), FirstSeen = min(TimeGenerated), LastSeen = max(TimeGenerated) by SourceIP, DestinationIP, DestinationPort, Activity
| extend DetectionRule = "iShareStuff-CTI-Compiled"
| sort by EventCount desc

2. Cyber Threat Intelligence & Forensik

🎯
MITRE ATT&CK Matrix Navigator 14 Taktiken
Reconnaissance
-
Resource Development
-
Initial Access
Execution
Persistence
-
Privilege Escalation
Defense Evasion
Credential Access
-
Discovery
-
Lateral Movement
-
Collection
-
Command and Control
Exfiltration
-
Impact
tsecurity.de Cognitive Threat RAG
Fokus-Vektor:

Kognitive Analyse für identifizierte Bedrohung: Erhöhte Bedrohungslage im Bereich The Silent Killers in Your Go App: Unhan.... Basierend auf 368k Vektor-Korrelationen werden sofortige Isolationsmaßnahmen für betroffene Endpunkte empfohlen.

🛡️ Angriffsfläche & Exposure

Netzwerk/Remote-Zugriff ohne Vorauthentifizierung möglich.

⚡ Empfohlene Sofortmaßnahmen
  • 1. Perimeter-Inspektion: Relevante Portfreigaben und exponierte Endpunkte unverzüglich scannen.
  • 2. Patch-Applikation: Hersteller-Hotfix einspielen oder betroffene Daemons in isolierte DMZ-Segmente überführen.
  • 3. Telemetrie & EDR-Alerts: Prozessaufrufe und Child-Processes auf anomale Shell-Spawns überwachen.
🔗 Semantisch verwandte Zero-Days MariaDB 11.7 VEC
Ähnliche Beiträge
🔍 Verwandte News

Auch interessante Nachrichten The Silent Killers in Your Go App: Unhandled Errors

Thematisch verwandte Begriffe: Silent, Killers, Your, Unhandled · 6 Treffer

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Zum Aktualisieren ziehen
ZERO-DAY CVE-2026-97898 | Insecure Direct Object Reference / missing object-level authorization in…
Advisory →
tsecurity.de Icon
Offline-Lesen, Eilmeldungen & 0ms Ladezeit

Installiere tsecurity.de direkt auf deinen Home-Bildschirm für das ultimative Vollbild-Magazinerlebnis ohne Browser-Leisten.

Nächster Beitrag