CLAUDE LABJP
FORK — Claude Code 2.1.212 changes what /fork does: it copies your conversation into a new background session with its own row in claude agents, so you can keep working. The old in-session subagent is now /subtaskLIMITS — WebSearch calls are now capped at 200 per session by default, and subagent spawns get the same 200 ceiling, so a runaway search or delegation loop stops on its ownMCPBG — MCP tool calls running past two minutes now move to the background automatically, keeping the session usable. Tune the threshold with CLAUDE_CODE_MCP_AUTO_BACKGROUND_MSPLANFIX — Fixed plan mode auto-running file-modifying Bash commands such as touch and rm without a permission prompt or an SDK canUseTool callbackSONNET5 — Claude Sonnet 5 is running on introductory pricing of $2 per million input tokens and $10 per million output. After August 31 it moves to $3 and $15IPO — Bankers are reportedly lining up investor meetings for Anthropic ahead of a possible public listing as soon as OctoberFORK — Claude Code 2.1.212 changes what /fork does: it copies your conversation into a new background session with its own row in claude agents, so you can keep working. The old in-session subagent is now /subtaskLIMITS — WebSearch calls are now capped at 200 per session by default, and subagent spawns get the same 200 ceiling, so a runaway search or delegation loop stops on its ownMCPBG — MCP tool calls running past two minutes now move to the background automatically, keeping the session usable. Tune the threshold with CLAUDE_CODE_MCP_AUTO_BACKGROUND_MSPLANFIX — Fixed plan mode auto-running file-modifying Bash commands such as touch and rm without a permission prompt or an SDK canUseTool callbackSONNET5 — Claude Sonnet 5 is running on introductory pricing of $2 per million input tokens and $10 per million output. After August 31 it moves to $3 and $15IPO — Bankers are reportedly lining up investor meetings for Anthropic ahead of a possible public listing as soon as October
Back to Blog

Reading Crashlytics Stack Traces With Claude — A Solo Developer's Morning Incident Notes

crashlyticsandroiddebuggingclaude-workflowindie-developer

Opening Firebase Crashlytics before I pour my first coffee has become a habit over the last few years.

I have been building wallpaper apps on my own since 2014, and cumulative downloads have passed fifty million. The numbers make me happy, but more users also means more crashes in absolute terms. And because I work alone, reading the stack traces that piled up overnight, forming a hypothesis, and shipping a fix are all my job and mine only.

For a long time, this morning routine was the loneliest part of my day. I would see a list of unfamiliar native crashes and have no idea where they came from. There was no one I could turn to and ask, "What do you think this is?" Putting Claude beside me for incident response has made that loneliness much lighter. Here is how I actually do it.

Don't read the stack trace — read it together

A Crashlytics report carries too much information, even when you are used to it: thread lists, native frames, obfuscated method names, the affected OS versions and devices. For years I tried to load all of that into my head before thinking about the cause.

Now I do it differently. When I open a crash that worries me, I paste the relevant part of the stack trace straight into Claude and ask:

Give me three first-pass hypotheses you can draw from this stack trace, ordered by likelihood. Also tell me what to check next to narrow down the blast radius.

The key is asking for "hypotheses ordered by likelihood," not "the answer." Crashes often look like they have one cause but actually involve several tangled factors, and a flat assertion narrows your field of view too early. Getting a ranked list plus "what to look at next" lets me assemble my own investigation steps.

Case 1: An IndexOutOfBoundsException in RecyclerView

In May 2026, right after I shipped v2.0.0 of the Android wallpaper app, more than fifty users hit an IndexOutOfBoundsException around RecyclerView within twenty-eight days. I had no reproduction steps, and it never happened once on my own device.

When I pasted the trace, Claude pointed out that "the data source is likely being rewritten on a background thread mid-update, so RecyclerView is referencing a stale index." My code looked like this:

// Before: the adapter referenced its internal list directly
class WallpaperAdapter(
    private val items: MutableList<Wallpaper>
) : RecyclerView.Adapter<WallpaperViewHolder>() {
 
    override fun onBindViewHolder(holder: WallpaperViewHolder, position: Int) {
        // if items is mutated on another thread, position drifts
        holder.bind(items[position])
    }
 
    override fun getItemCount(): Int = items.size
}

The problem was that I was mutating the items list from outside the screen (a download-completion callback, among others). When the contents change before notifyDataSetChanged() runs, the index drifts between getItemCount() and onBindViewHolder().

Talking it through with Claude, I rewrote it so every update hands over a defensive copy.

// After: updates always swap in an immutable list
class WallpaperAdapter : RecyclerView.Adapter<WallpaperViewHolder>() {
 
    private var items: List<Wallpaper> = emptyList()
 
    // the only way to update from the outside
    fun submit(newItems: List<Wallpaper>) {
        // take a defensive copy before swapping
        items = newItems.toList()
        notifyDataSetChanged()
    }
 
    override fun onBindViewHolder(holder: WallpaperViewHolder, position: Int) {
        holder.bind(items[position])
    }
 
    override fun getItemCount(): Int = items.size
}

It is a tiny change, but making submit() the single entry point for updates made list swaps atomic. After a staged rollout of v2.1.0, this crash disappeared from Crashlytics entirely. The shift from "trying to reproduce" an unreproducible crash to "building a structure that doesn't break even if it does" was something I managed to put into words during the conversation with Claude.

Case 2: A NoClassDefFoundError gone in one line

There is another morning that stuck with me. After bumping the image library Glide to 5.0.5 and updating the Android Gradle Plugin to the 9.x line, certain old devices started crashing right at launch. The stack trace had java.lang.NoClassDefFoundError and java.util.function.Supplier side by side.

The impact was concentrated among Android 6.0.1 users. When I told Claude that, it came back with: "Supplier is a Java 8 API that doesn't exist below API level 24. The library is probably referencing a Java 8 class internally while desugaring isn't taking effect."

It was exactly right. The fix was a single line in build.gradle.

android {
    compileOptions {
        // desugar Java 8+ APIs for older devices
        coreLibraryDesugaringEnabled true
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
}
 
dependencies {
    coreLibraryDesugaring "com.android.tools:desugar_jdk_libs:2.0.4"
}

That one line wiped out every crash my Android 6.0.1 users had been hitting. What stayed with me was how much faster I reached the cause compared to digging through official docs alone. Claude didn't assert the answer; starting from the "OS version skew" I had overlooked, it helped me reframe the right question.

The rules I keep during incident response

This isn't a story about pasting anything in and having it solved. As I folded Claude into solo incident response, I settled on a few rules.

First, always bring the numbers about blast radius. Adding "how many users," "which OS versions," and "since which release" changes the precision of the hypotheses completely. Just as the OS skew was the key to the NoClassDefFoundError, the numbers themselves are clues.

Second, verify fixes with a staged rollout. I release wallpaper-app updates in the order 5% to 25% to 50% to 100%, advancing only while crash-free users stay above 99.7% and ANR stays under 0.20%. Even when a fix is Claude's suggestion, I treat it as a hypothesis until it is verified against real users.

Third, keep the final call yourself. I am the one who writes the code and bears the responsibility of shipping it. Claude is a wonderful sounding board, but I believe the person pressing deploy has to be me.

If you want context on the everyday workflow with Claude Code, reading a checklist for when Claude Code won't run alongside this might help with isolating environment-side issues. And if you want to track revenue and crash rate on one screen, I also recommend the piece on building a mobile app revenue dashboard with Claude Code.

What to try first tomorrow morning

If you are carrying a crash you can't explain right now, when you open Crashlytics tomorrow, try pasting the stack trace into Claude together with "the distribution of affected OS versions." Before the cause itself, you may find the skew you had been overlooking.

When you develop alone, there will always be mornings with no one to ask. I am still feeling my way through a lot of this myself, but if it lightens even a little of the loneliness for someone else facing incidents on their own, I'll be glad. Thank you for reading.