◉RORK LABJP
●SDK58 — Expo SDK 58 is still in beta (September 15). Stable lands after React Native 0.88 ships, planned for October 12, and Expo Modules 2.0 stays beta until then●10/22 — 24 days until Apple opens Volume Purchasing. Multiseat purchases have been on by default since September 16, so decide now whether to keep them●REVIEW — A common question: the automated pre-review check flags a missing entitlement you never added. One developer opened the .ipa, confirmed it, and passed without a rebuild●NEW — Building the same list screen with Opus 5.5 and GPT-6 Sol, and how we chose effort●NATIVE — After Shopify's back-to-native post, someone built the same app in React Native and native and measured on device. Useful input for any migration debate●CREDITS — How far does "no credits for AI errors" actually go? We are logging a day where the same fix was requested three times to draw the line●SDK58 — Expo SDK 58 is still in beta (September 15). Stable lands after React Native 0.88 ships, planned for October 12, and Expo Modules 2.0 stays beta until then●10/22 — 24 days until Apple opens Volume Purchasing. Multiseat purchases have been on by default since September 16, so decide now whether to keep them●REVIEW — A common question: the automated pre-review check flags a missing entitlement you never added. One developer opened the .ipa, confirmed it, and passed without a rebuild●NEW — Building the same list screen with Opus 5.5 and GPT-6 Sol, and how we chose effort●NATIVE — After Shopify's back-to-native post, someone built the same app in React Native and native and measured on device. Useful input for any migration debate●CREDITS — How far does "no credits for AI errors" actually go? We are logging a day where the same fix was requested three times to draw the line
Articles/AI Models
◉ AI Models/2026-09-28Beginner

Keep Fixing in the Same Thread, or Restore and Rephrase? What I Learned Fixing One Rork Bug Both Ways

When a Rork fix request fails for the third time, do you keep going or use Restore and ask differently? I fixed the same bug both ways and compared round trips, leftover code, and where I now draw the line.

Rork573AI31no-code27indie developer40SwiftUI66

It was late, and I was prototyping a tiny screen in Rork: pick a sound to fall asleep to, set a number of minutes, and have it stop on its own. I've run a healing-sounds app for a long time as an indie developer, which means I carry a lot of assumptions about how "stop when the time is up" should work. That's exactly why I wanted to watch an AI build it from nothing.

The first generation was lovely. Fifteen minutes in, fifteen minutes later, the sound stopped while I stared at the screen. Then I went back to the home screen, reopened the timer screen, and sometimes it never stopped at all. That's where three rounds of fix requests began.

The first time I wrote "it sometimes doesn't stop." The second time, "it still doesn't stop." Halfway through typing the third message, my hands paused. Do I send this as the next message in the same thread, or do I go back to the version that worked and ask in a different way? I ended up doing both, so here's what happened and the line I've drawn since.

The short version: from the third attempt on, I go back

Let me put the conclusion first. When a fix request for the same bug reaches its third round, I stop asking in the same thread. I restore the last version that worked and ask again with different words.

Rork's FAQ says the Restore feature lets you revert to any previous version, and that you can preview each version before restoring so you pick the right one. The same FAQ lists three ways out of an error loop: ask Rork to search the web, switch to a different model, or revert to a working version and try the feature again with a different approach.

One thing I had misread at first: the FAQ also says AI errors are not charged as credits. The way I read it now, that covers cases where the generation itself fails. A fix that runs but does the wrong thing, like mine, costs a normal round trip. With the Free plan at 5 credits per day and Pro at 100 per month, rounds that "didn't actually fix it" eat the balance faster than you'd expect.

I've written separately about how the free allowance behaves in Rork's Free Tier Gives You 35 Credits a Month, but the Number That Shapes Your Work Is 5 a Day, so here I'll only count round trips.

Path one: keep fixing in the same thread, and what the code looked like after

First, I sent the third message anyway: "Make sure it always stops at the set time, even if I reopen the screen."

The fix came back and it worked. But when I opened the code in Dev mode, this is roughly what was left. I've trimmed it to the essentials.

// Before: what remained after three rounds of "please fix it" (essentials only)
final class SleepTimer: ObservableObject {
    @Published var isPlaying = false
    private var timer: Timer?
    private var backupTimer: Timer?        // added in round two
    private var shouldStop = false         // added in round three
 
    func start(minutes: Int) {
        isPlaying = true
        let seconds = Double(minutes) * 60
        timer = Timer.scheduledTimer(withTimeInterval: seconds, repeats: false) { _ in
            self.isPlaying = false
        }
        backupTimer = Timer.scheduledTimer(withTimeInterval: seconds + 1, repeats: false) { _ in
            self.shouldStop = true
        }
        DispatchQueue.main.asyncAfter(deadline: .now() + seconds) {
            if self.shouldStop { self.isPlaying = false }
        }
    }
}

Three separate mechanisms for stopping, all alive at once: the original timer, a backup timer from round two, and a flag plus a delayed block from round three. The real cause, that start ran every time the screen reappeared and scheduled a new deadline without cancelling the old one, was never touched.

Here's how I understand why this happens. When you keep asking in the same thread, the AI treats the conversation so far as a correct premise. It's unlikely to conclude that its own previous fix was wrong; adding one more layer on top is the more natural continuation of the dialogue. I suspect a human colleague told "still broken" three times would react the same way.

Three round trips, working code, and something I could no longer read. If another bug showed up, I had no confidence I could tell which of the three paths was the culprit.

Path two: restore, then rephrase

So I opened Restore, previewed the version from just before my first fix request, and reverted to it. That version still had the reopen bug, untouched.

Then I asked about the same bug exactly once, in different words. Looking at the three failed messages, what stood out was that I'd been describing a symptom rather than the state I wanted. I narrowed the rephrasing down to three moves.

# Three rephrasing moves (I rebuilt my actual message along these lines)
 
1. Describe the outcome, not the symptom
   "it sometimes doesn't stop" → "once the stop time has passed, playback must be
    stopped no matter which screen transitions happened in between"
 
2. Name the single place where the decision lives
   "keep the stop time as one value, and make the only stop check a comparison
    against that value"
 
3. Say what not to do
   "do not create more than one timer, do not add flags, and if a stop time already
    exists when the screen reopens, do not extend it"

What came back looked like this.

// After: one stop time, one place that checks it
import Foundation
import Combine
 
@MainActor
final class SleepTimer: ObservableObject {
    @Published private(set) var isPlaying = false
    private var stopAt: Date?
    private var ticker: AnyCancellable?
 
    func start(minutes: Int) {
        // Never extend an existing schedule (prevents double-start on reopen)
        guard stopAt == nil else { return }
        stopAt = Date().addingTimeInterval(TimeInterval(minutes * 60))
        isPlaying = true
        ticker = Timer.publish(every: 1, on: .main, in: .common)
            .autoconnect()
            .sink { [weak self] now in self?.check(now) }
    }
 
    func stop() {
        isPlaying = false
        stopAt = nil
        ticker?.cancel()
        ticker = nil
    }
 
    private func check(_ now: Date) {
        guard let stopAt, now >= stopAt else { return }
        stop()
    }
}

I prefer this shape because a single date, stopAt, tells me what will happen next. If the sound doesn't stop, there are only two places to suspect: stopAt is nil, or check isn't being called. Compared with the three-layer version, the load on future me is far lighter.

Round trips: one restore, one rephrased request. The restore itself didn't consume credits, so one round trip was all it cost.

The two paths side by side

Same bug, same evening, both paths. These numbers come from my own notes and will obviously vary with the project and the kind of bug.

What to look atKeep fixing in the threadRestore and rephrase
Fix-request round trips3 (fixed on the third)1 (restore is separate and costs no credits)
Stopping mechanisms in the code3 running in parallel1
Touches the real cause?No; each fix patches the symptomYes; a guard prevents the double start
Debugging the next failureTrace three pathsTwo places: stopAt and check
Work lostNoneAnything after the restored version (zero this time)
My own understandingCan't explain what was fixedMy prompting mistake became words

The table makes restore look like a landslide, but I don't think it's that simple. There are situations where staying in the thread is clearly the better path.

One is when the error messages are specific and each round visibly moves forward. If build errors change line numbers and shrink in count, continuing is faster. The other is when you've stacked several other features on top since the bug appeared. Restore rewinds those too, so you either export that work first, or give up on restoring and narrow the symptom description instead.

Where I draw the line

I don't want to end on the contrast alone, so here's how I keep both paths and choose between them.

Two rounds in the thread; on the third, go back and rephrase. The reason is simple. After two failures I have enough material to put into words what was wrong with my request. Going back after one failure is too early and I have nothing to learn from. Waiting until the fourth lets the layers pile up and makes it harder to pick which version to return to.

And the one request after restoring must not repeat the symptom. I pull the desired state, the single decision point, and the things not to do from the three failures, and fold them into one message. Working alone, I have no one at the next desk to ask, so this rephrasing step doubles as an inventory of what I actually understand.

Since drawing this line, the sinking feeling in my stomach when I look at the credit balance after a string of fixes has mostly gone. Walking both paths on the same night is still what my judgement rests on.

One next step

If you have a bug that "three requests couldn't fix," open Restore and preview the version that worked, just once. You don't have to revert. Seeing the working screen makes it much easier to say, in words, where the current behavior departs from what it should be.

Deciding whether to actually revert can wait until after that.

One more thing. The line between what I hand to the AI and what I keep as my own decision got another notch more complicated once Rork started producing two separate native apps for iPhone and Android. I've written up how I actually split that in Where the AI decision lives once Rork splits your app into two native codebases.

Share

Thank You for Reading

Rork Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • ✦Copy-paste ready implementation code
  • ✦New advanced guides published daily
  • ✦$5/mo or $15 for lifetime access
View Membership →

If you found this article helpful, a small tip ($1.50) would mean a lot to us. Your support helps keep this site ad-free and covers server and hosting costs.

Related Articles

◉ AI Models2026-07-05
When Your Finance App's AI Keeps Dumping Everything Into 'Other' — Field Notes on Catching Silent Classification Drift
An AI expense classifier in a Rork finance app can be accurate at launch and quietly decay over months until the monthly advice goes wrong. Here is how I instrumented confidence and category distribution to get ahead of the drift, with working code.
◉ AI Models2026-06-27
Monetizing a Rork-Built App — Choosing Between Ads, Subscriptions, and Freemium
How to monetize an app built with Rork — from choosing between ads, subscriptions, freemium, and one-time purchase to the implementation details. Phased AdMob formats, treating ad-free as a single source of truth, and price anchoring, written from the indie-developer trenches.
◉ AI Models2026-06-17
Where to Stop Letting Rork Fix Your Bugs: A Triage Routine for the 30% That Need You
Most bugs you hand Rork get fixed in a couple of regenerations. A stubborn minority loop forever, each fix spawning a new symptom. Here is the triage routine I use to split what to delegate from what to take over by hand, with retreat lines, regression guards, and a decision log.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links