Skip to content

Surface row codec errors instead of silently returning empty rows#120

Open
mweiden wants to merge 1 commit into
mainfrom
claude/audit-fix-13-row-codec-errors
Open

Surface row codec errors instead of silently returning empty rows#120
mweiden wants to merge 1 commit into
mainfrom
claude/audit-fix-13-row-codec-errors

Conversation

@mweiden

@mweiden mweiden commented Jun 9, 2026

Copy link
Copy Markdown
Owner

Problem

encode_row/decode_row swallowed serde_json errors with unwrap_or_default(), so a corrupted stored row decoded to an empty map — indistinguishable from a legitimately deleted row. On-disk corruption surfaced as silent data loss ("all columns gone") instead of an error anyone could detect.

Fix

  • Both functions return Result<_, RowCodecError> (new error type in schema.rs).
  • Empty input remains a valid empty row — tombstones and absent rows are not corruption and keep working exactly as before. Only non-empty data that fails to parse is an error.
  • QueryError gains a transparent Codec variant; the query engine and the cluster LWT paths propagate it.
  • Read repair, which is best-effort, logs and skips an undecodable replica value rather than aborting the whole repair pass.
  • build_row now returns the column map and callers encode it, so encode failures propagate there too.

Tests

  • corrupt_row_surfaces_error_instead_of_empty_row: garbage bytes in a stored row turn a SELECT into an error, not an empty result.
  • deleted_row_still_reads_as_absent: tombstone behavior unchanged.
  • Unit tests for round-trip, empty-input, and corrupt-input cases.

Found by codebase audit (medium severity: corruption masked as silent data loss).

https://claude.ai/code/session_01CJXWjB9ERGgV6B9mRqrdM9


Generated by Claude Code

encode_row/decode_row swallowed serde_json errors with
unwrap_or_default(), so a corrupted stored row decoded to an empty map
- indistinguishable from a legitimately deleted row. Corruption showed
up as silent data loss ('all columns gone') instead of an error.

Both functions now return Result. Empty input remains a valid
representation of an absent/deleted row (tombstones), but non-empty
data that fails to parse is reported as a QueryError. Read repair
logs and skips undecodable replica values rather than aborting the
whole repair pass.

https://claude.ai/code/session_01CJXWjB9ERGgV6B9mRqrdM9

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 452d01700c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/cluster.rs
if !val.is_empty() {
let map = decode_row(val.as_bytes());
arr_rows.push(map);
arr_rows.push(decode_row(val.as_bytes())?);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Surface codec failures from partial replica reads

When one replica returns the new QueryError::Codec but another replica returns a valid Meta row, merge_results records the error in last_err and then enters this rows branch, returning the decoded rows and never surfacing the corruption. In an RF>1 read where a corrupt replica is contacted but enough other replicas satisfy the read consistency, this still silently hides the row codec error that this change is intended to expose; consider prioritizing codec errors before returning merged rows.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants