JuliusBrussee/caveman · error

read applied fix id

Error message

read applied fix id: %w

What it means

After a successful INSERT, RecordAppliedFix calls result.LastInsertId() to get the new row's ID. If the driver cannot report it the error is wrapped as "read applied fix id". SQLite via mattn/go-sqlite3 or modernc supports this, so failure indicates a driver/connection-level problem.

Solutions

  1. Verify the registered driver supports LastInsertId for INSERT (use mattn/go-sqlite3 or modernc.org/sqlite)
  2. Read the wrapped %w error to confirm the driver-level cause
  3. If the driver can't provide it, query last_insert_rowid() in the same connection instead

Example fix

// before
id, err := result.LastInsertId()
// after
var id int64
err = s.db.QueryRow("SELECT last_insert_rowid()").Scan(&id)
Defensive patterns

Strategy: try-catch

Validate before calling

// ensure driver supports LastInsertId: use modernc.org/sqlite or github.com/mattn/go-sqlite3

Try / catch

if _, err := store.RecordAppliedFix(ctx, sink, kind, note); err != nil { if strings.Contains(err.Error(), "read applied fix id") { log.Error("driver lacks LastInsertId support; check sqlite driver") } }

Prevention

When it happens

Trigger: LastInsertId() returning an error on the just-executed INSERT result — typically with a driver/pool that doesn't support LastInsertId (e.g. some proxies or non-SQLite drivers behind database/sql).

Common situations: Swapping the SQLite driver for one lacking LastInsertId support; using the result after the connection was reset.

Related errors


AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20). Data as JSON: /api/errors/d4b59563ff47e87b. Report an issue: GitHub.

Appendix: source

Thrown at proxy/internal/store/learn_outcomes.go:114

	// `caveman learn applied` time is immediately after the approved edit. This
	// is the provenance anchor: a later scan re-hashes the same path and can
	// state whether the change we proposed is still the change that is there.
	target := fingerprintFixTarget(*sink)
	targetJSON := ""
	if target != nil {
		if encoded, encodeErr := json.Marshal(target); encodeErr == nil {
			targetJSON = string(encoded)
		}
	}
	result, err := s.db.Exec(`INSERT INTO applied_fixes
		(sink_id, practice_id, fix_kind, applied_at, before_tokens_per_turn, before_evidence_json, note, target_json)
		VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, sink.SinkID, sink.PracticeID, fixKind, appliedAt, sink.TokensPerTurn, string(evidence), note, targetJSON)
	if err != nil {
		return AppliedFixRecord{}, fmt.Errorf("record applied fix: %w", err)
	}
	id, err := result.LastInsertId()
	if err != nil {
		return AppliedFixRecord{}, fmt.Errorf("read applied fix id: %w", err)
	}
	return AppliedFixRecord{
		Recorded: true, ID: id, SinkID: sink.SinkID, PracticeID: sink.PracticeID,
		FixKind: fixKind, AppliedAt: appliedAt, BeforeTokensPerTurn: sink.TokensPerTurn,
		BeforeEvidence: beforeEvidence, BeforeSource: beforeSource, Note: note,
		Target: target,
	}, nil
}

func beforeSinkForAppliedFix(plan LearnPlan, sinkID string, live *Sink, fallbackCutoff time.Time) (*Sink, string) {
	cutoff := fallbackCutoff
	if parsed, err := time.Parse(time.RFC3339Nano, plan.computedAt); err == nil {
		cutoff = parsed
	}
	if snapshot := newestLearnSnapshotSink(sinkID, cutoff); snapshot != nil {
		return snapshot, "report_snapshot"
	}
	if live != nil {

View on GitHub (pinned to 3ee70a1026)