Hmbown/CodeWhale · error · NSError(CodewhalePet)

1

1

Error message

The particle checkpoint does not match this whale.

What it means

PetSim throws NSError(domain: "CodewhalePet", code: 1) with this message at PetSim.swift:301 when a deserialized particle checkpoint fails structural validation: wrong version, expressionVersion outside {1,2}, body/particle-array length mismatched with the live particle count, color not exactly 3 channels, channel indices out of range, per-particle body triplets not matching the current particle state (hx, hy, s), or non-finite particle values. The store refuses to apply a checkpoint that does not describe this exact whale.

Solutions

  1. Delete or rename the incompatible `<source>.json` (and stale `-segment-` archives) so the app starts a fresh checkpoint.
  2. Verify the checkpoint belongs to the same whale — do not copy habitat files between accounts/devices.
  3. Upgrade or downgrade the app so its expressionVersion set matches the checkpoint's (accepted: 1 or 2, defaulting to 1).
  4. If you produce checkpoints programmatically, ensure body triplets equal the live particles' (hx, hy, s) and all particle fields are finite before saving.

Example fix

// before: blindly applying a foreign checkpoint
try pet.apply(checkpoint)

// after: pre-validate shape and count against the live whale
guard checkpoint.particles.count == pet.particleCount,
      checkpoint.version == 1 else {
    logger.warning("checkpoint incompatible; resetting")
    try? FileManager.default.removeItem(at: habitatFile)
    return
}
try pet.apply(checkpoint)
Defensive patterns

Strategy: validation

Validate before calling

// Validate checkpoint shape before applying
func checkpointMatches(_ c: Checkpoint, p: [Particle]) -> Bool {
    c.version == 1
        && [1, 2].contains(c.expressionVersion ?? 1)
        && c.body.count == p.count && c.particles.count == p.count
        && c.color.count == 3
        && c.particles.allSatisfy { $0.count == 8 && $0.allSatisfy(\.isFinite) }
}

Type guard

func isCheckpointMismatch(_ e: Error) -> Bool {
    let n = e as NSError
    return n.domain == "CodewhalePet" && n.code == 1
        && n.localizedDescription.contains("checkpoint does not match")
}

Try / catch

do { try pet.apply(checkpoint) }
catch let e as NSError where isCheckpointMismatch(e) {
    logger.warning("incompatible checkpoint; resetting habitat")
    try? resetHabitat()
}

Prevention

When it happens

Trigger: Applying a checkpoint from a different whale/pet instance; loading a save written by an older or newer version with a different particle count or expression version; hand-edited or truncated JSON in the habitat store; corrupted archive segments.

Common situations: Restoring a backup from another device whose pet evolved differently; version migration between app releases changed checkpoint layout; file corruption from an interrupted sync.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@433685b202 (2026-09-15). Data as JSON: /api/errors/693a9984769b04d8. Report an issue: GitHub.

Appendix: source

Thrown at pet/swift/PetSim.swift:301

            q.ang = atan2(q.hy, q.hx)
            q.rad = (q.hx * q.hx + q.hy * q.hy).squareRoot()
            q.tail = clamp01(((-q.hx - q.hy) * 0.5 + 0.22) / 0.62)
            return q
        }
        cur = channelIndex("reasoning")!
        prev = cur
    }

    func restoreValidated(_ checkpoint: PetParticleCheckpoint) throws {
        // Array and identity checks also protect this native boundary if its
        // caller changes. Mutation starts only after the complete shape passes.
        guard checkpoint.version == 1, [1, 2].contains(checkpoint.expressionVersion ?? 1), checkpoint.body.count == p.count,
              checkpoint.particles.count == p.count, checkpoint.color.count == 3,
              CHANNELS.indices.contains(checkpoint.previous), CHANNELS.indices.contains(checkpoint.current),
              checkpoint.body.enumerated().allSatisfy({ i, v in
                  v.count == 3 && v[0] == p[i].hx && v[1] == p[i].hy && v[2] == p[i].s
              }), checkpoint.particles.allSatisfy({ $0.count == 8 && $0.allSatisfy(\.isFinite) })
        else { throw NSError(domain: "CodewhalePet", code: 1, userInfo: [NSLocalizedDescriptionKey: "The particle checkpoint does not match this whale."]) }
        expressionVersion = checkpoint.expressionVersion ?? 1
        phase = checkpoint.phase; clock = checkpoint.clock; tear = checkpoint.tear
        prev = checkpoint.previous; cur = checkpoint.current
        col = (checkpoint.color[0], checkpoint.color[1], checkpoint.color[2]); frame = checkpoint.frame
        for i in p.indices {
            let v = checkpoint.particles[i]
            p[i].x = v[0]; p[i].y = v[1]; p[i].vx = v[2]; p[i].vy = v[3]
            p[i].jx = v[4]; p[i].jy = v[5]; p[i].tx = v[6]; p[i].ty = v[7]
        }
    }

    /// Advance the sim by dt seconds under `state`. Identical math to PetSim.ts.
    public func step(dt: Double, state: PetState, motion: Bool = true, sensitivity: Double = 1, podSlots: [(Int, Double)]? = nil) {
        let s: (Double) -> Double = { lerp(0.5, $0, sensitivity) }
        let act = s(state.activity), coh = s(state.coherence), att = s(state.attention)
        let seen = s(state.observed)
        let mot = motion ? 1.0 : 0.0
        phase += dt * (0.18 + act * 0.55) * mot

View on GitHub (pinned to 433685b202)