sql: serialize Date parameters as ISO 8601 in Postgres text format#29013
sql: serialize Date parameters as ISO 8601 in Postgres text format#29013robobun wants to merge 5 commits into
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
WalkthroughStandardizes ISO string buffering and changes Date serialization to emit ISO 8601 for PostgreSQL binds; updates native bindings to accept caller-provided buffers and adds a regression test for Date parameter round-trips. Changes
🚥 Pre-merge checks | ✅ 4✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Comment |
|
Updated 9:37 AM PT - May 1st, 2026
❌ @robobun, your commit 389dd7c has 2 failures in
DetailsDetails🧪 To try this PR locally: bunx bun-pr 29013That installs a local version of the PR into your bun-29013 --bun |
There was a problem hiding this comment.
This PR correctly fixes Date serialization in text-format Bind messages, but the inline comments highlight a pre-existing binary-format/text-data mismatch for .time, .float4, .float4_array, and .numeric types in the same else branch — worth a human look before merging.
Extended reasoning...
Overview\nModifies 4 files: fixes a pre-existing bug in bindings.cpp (ISO string was written to a local buffer instead of the caller's), widens the Zig buffer declaration to match, adds a isDate() guard in writeBind's else branch to emit ISO 8601 instead of the locale-dependent toString() output, and adds a regression test.\n\n### Security risks\nNone — this is a serialization bug fix with no security implications.\n\n### Level of scrutiny\nThe fix itself is straightforward and correct. However, the flagged pre-existing issue (binary format code 1 declared in the first loop for .time/.float4/.float4_array/.numeric, but text bytes sent in the else branch) is a real protocol mismatch in the same code path being modified. The new comment "This path runs in text format" is misleading for those types. This warrants human review to decide whether to fix or defer the pre-existing mismatch.\n\n### Other factors\nThe test's afterAll unconditionally calling dockerCompose.down() when the local-postgres fallback was used is a minor nit. The build is in progress (#44483). No prior human reviewer comments to address.
fae0fcc to
6dfd18c
Compare
There was a problem hiding this comment.
LGTM — the buffer fix and the guard in are both correct and well-targeted.
Extended reasoning...
Overview
Fixes a regression where JS Date parameters sent via the text-format path (prepare: false, or first execution of a fresh prepared statement) were serialized using Date.prototype.toString() instead of Date.prototype.toISOString(). The fix has three parts: (1) remove the local char buffer[64] in JSC__JSValue__toISOString that shadowed the caller's output buffer (plain C bug), (2) widen the Zig-side buffer from [28]u8 to [64]u8 to match the C contract, and (3) add an isDate() check in the else branch of writeBind that emits ISO 8601 instead of falling through to String.fromJS.
Security risks
None. The change only affects serialization of Date values in PostgreSQL protocol Bind messages. No auth, crypto, or permission paths are touched.
Level of scrutiny
Low-to-medium. The C++ and Zig changes are each 1-3 lines and address clear bugs. The Zig request change is additive (a Date-specific fast path before the existing text fallback). The test file properly tracks whether Docker was used and only tears down what it started.
Other factors
All bugs flagged by the hunt system are pre-existing and not introduced by this PR. The inline comment about unconditional dockerCompose.down() was already addressed in the committed test code (target.usedDocker flag). CI failures are all in unrelated valkey/azure-service-bus tests.
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/bun.js/bindings/JSValue.zig`:
- Around line 408-423: Extract the 64-byte ISO buffer into a single exported
type/constant on JSValue (e.g., pub const ISOStringBuffer = [64]u8) and replace
the literal [64]u8 occurrences in the extern declarations and function
signatures (JSC__JSValue__toISOString, toISOString,
JSC__JSValue__DateNowISOString, getDateNowISOString) with that named type (use
pointer form where needed, e.g., *jsc.JSValue.ISOStringBuffer). Update call
sites to declare buffers as var iso_buf: jsc.JSValue.ISOStringBuffer = undefined
and keep existing slicing/return logic unchanged but operating on the typed
buffer. Ensure the extern prototypes, function params, and callers all reference
the new ISOStringBuffer symbol to avoid future drift.
In `@src/sql/postgres/PostgresRequest.zig`:
- Around line 162-181: When value.isDate() is true, do not fall back to the old
String.fromJS path on ISO serialization/write failure; instead treat any ISO
serialization or write failure as a bind error and return it immediately.
Specifically, in the PostgresRequest.zig block that calls value.toISOString(...)
and then writer.write(iso) (and the subsequent l.writeExcludingSelf()), if
iso.len == 0 or any of those operations would fail, propagate/return a
bind-specific error (e.g., error.BindError) rather than clearing handled and
continuing to the String.fromJS branch; this prevents re-entering the old text
path for Date values and ensures failures for Date binding are reported as bind
errors.
In `@test/regression/issue/29010.test.ts`:
- Around line 69-74: The test teardown calls dockerCompose.down() in afterAll
which brings down the whole shared bun-test-services project; instead, change
the cleanup to only release the Postgres service acquired by resolvePostgres()
(postgres_plain) or use a refcounted release helper so we don't stop unrelated
services: replace the dockerCompose.down() call with a service-scoped operation
(e.g. stop/remove or release only 'postgres_plain') or wire in a refcounted
release function that decrements and only tears the project down when no suites
remain, ensuring afterAll only targets postgres_plain rather than the entire
project.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: aee2e565-66f3-489d-9739-622574e760b2
📒 Files selected for processing (4)
src/bun.js/bindings/JSValue.zigsrc/bun.js/bindings/bindings.cppsrc/sql/postgres/PostgresRequest.zigtest/regression/issue/29010.test.ts
There was a problem hiding this comment.
LGTM — all raised issues addressed (afterAll guard added, toISOString buffer fixed, DateNowISOString ABI fixed in 0ffeaa6); pre-existing binary-format mismatches acknowledged as out-of-scope.
Extended reasoning...
Overview
Fixes Date parameter serialization in PostgreSQL text-format path (prepare:false and first execution of prepared statements). Four files: bindings.cpp (one-line toISOString buffer fix), JSValue.zig (extern sig + buffer size corrections), PostgresRequest.zig (Date detection in else branch), and a new regression test.
Security risks
None — this is pure data serialization logic with no auth, crypto, or permission changes.
Level of scrutiny
Low. The fix is targeted and mechanical: detect isDate(), call toISOString(), fall back to the existing String.fromJS path otherwise. The C++ fix (remove shadowing local buffer) is a single-line change with obvious correctness. Both have corresponding test coverage.
Other factors
All three issues flagged in my prior review run have been addressed: the afterAll/dockerCompose guard is now conditional on usedDocker, the toISOString local-buffer bug is fixed, and the DateNowISOString ABI mismatch was fixed in the follow-up commit already merged to main. The remaining pre-existing binary-format mismatches (time/float4/int4_array) are correctly deferred.
There was a problem hiding this comment.
LGTM — all prior review concerns addressed in the follow-up commits.
Extended reasoning...
Overview
The PR fixes a real user-visible bug: Date objects passed as SQL parameters under prepare: false were serialized via Date.prototype.toString() (locale-dependent) rather than Date.prototype.toISOString() (RFC 3339), causing PostgreSQL to reject them with 22007 invalid input syntax. The fix is in the else branch of writeBind in PostgresRequest.zig, with supporting fixes in C++/Zig bindings and a regression test suite.
Security Risks
None. This is a serialization correctness fix for a database client library. No auth, crypto, or permissions code is touched.
Level of Scrutiny
Medium. The fix is targeted and the logic is straightforward (detect isDate(), call toISOString(), propagate error on failure). The C++ fix (using caller-provided buf instead of a local stack buffer) is a clear one-liner correction. The Zig ABI fix corrects a broken extern declaration that would have crashed on any call. The codegen tombstone change is mechanical.
Addressed Feedback
All three issues raised in my prior review were addressed: the getDateNowISOString ABI mismatch (fixed in 0ffeaa6), the unconditional dockerCompose.down() in afterAll (fixed in 6dfd18c), and the String.fromJS fallback after Date detection (fixed in 6014efd). The remaining pre-existing issues (.time/.float4/etc. binary-text mismatch) are pre-existing, unrelated to this PR, and correctly deferred.
There was a problem hiding this comment.
LGTM — previous concerns addressed.
Extended reasoning...
Overview
This PR fixes a real user-impacting bug: JavaScript Date parameters passed to PostgreSQL were serialized via toString() (locale-dependent) instead of toISOString() (ISO 8601), causing parse failures with SQLSTATE 22007. The change is targeted and correct: detect isDate() in the writeBind else-branch and emit ISO 8601 bytes, returning error.InvalidQueryBinding on failure rather than silently falling back.
Security risks
None. The change is in local serialization logic with no user-controlled code paths beyond what was already reachable.
Level of scrutiny
Moderate. This touches production SQL parameter serialization (PostgresRequest.zig) and a C++ binding (bindings.cpp). The core logic change is small and correct. The C++ fix (passing caller's buf to Bun::toISOString instead of a discarded local buffer) is a straightforward one-line correction.
Prior review feedback
All substantive issues from my earlier reviews have been addressed: the ABI mismatch in getDateNowISOString was fixed (0ffeaa6), the afterAll teardown no longer calls dockerCompose.down() (598822b), and the String.fromJS fallback was removed in favor of a hard bind error (6014efd). Pre-existing binary format mismatches for .float4/.time/.numeric/.int4_array are acknowledged out of scope. The only remaining nit (the prepare:true test exercises the text path, not binary) is a test coverage quality issue noted in an inline comment.
|
@robobun rebase main and fix whatever went wrong with the cron stuff. this pr doesn't need to touch cron |
598822b to
1a22fdd
Compare
|
✅ Pushed CI #49712: 70 jobs passed. Remaining failures are known flakes currently hitting multiple unrelated open PRs (#30041, #28645, #29897, #28862):
Commits:
Gate (local Postgres): MySQL |
There was a problem hiding this comment.
♻️ Duplicate comments (1)
src/bun.js/bindings/JSValue.zig (1)
408-423: 🧹 Nitpick | 🔵 TrivialDeduplicate the 64-byte ISO buffer contract.
*[64]u8is still repeated across both externs and wrappers here. Please lift it into a single exported alias/constant onJSValueand reuse it so the Zig↔C++ buffer contract cannot drift again.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/bun.js/bindings/JSValue.zig` around lines 408 - 423, The 64-byte buffer type `*[64]u8` is duplicated across the externs and wrappers (JSC__JSValue__toISOString, JSC__JSValue__DateNowISOString and the Zig functions toISOString and getDateNowISOString); create a single exported alias or constant on the JSValue type (e.g., ISO_BUFFER or IsoBufType) that defines the `[64]u8` shape and replace all occurrences in the extern declarations and the two public functions toISOString and getDateNowISOString to use that alias so the Zig↔C++ buffer contract is centralized and cannot drift.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Duplicate comments:
In `@src/bun.js/bindings/JSValue.zig`:
- Around line 408-423: The 64-byte buffer type `*[64]u8` is duplicated across
the externs and wrappers (JSC__JSValue__toISOString,
JSC__JSValue__DateNowISOString and the Zig functions toISOString and
getDateNowISOString); create a single exported alias or constant on the JSValue
type (e.g., ISO_BUFFER or IsoBufType) that defines the `[64]u8` shape and
replace all occurrences in the extern declarations and the two public functions
toISOString and getDateNowISOString to use that alias so the Zig↔C++ buffer
contract is centralized and cannot drift.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: d7acc210-1f34-4b60-a94d-450c033f173e
📥 Commits
Reviewing files that changed from the base of the PR and between 598822ba41b8af183c9919bc0ddca51c14e1f578 and 1a22fdd.
📒 Files selected for processing (2)
src/bun.js/bindings/JSValue.zigsrc/bun.js/bindings/bindings.cpp
|
@robobun adopt |
1a22fdd to
2c5d8ae
Compare
|
✅ No merge conflicts detected when merging into Your branch is good to go! |
1 similar comment
|
✅ No merge conflicts detected when merging into Your branch is good to go! |
2c5d8ae to
df43c24
Compare
There was a problem hiding this comment.
LGTM — all prior review feedback addressed. The NaN issue in date.zig is pre-existing and out of scope for this change.
Extended reasoning...
Overview
This PR fixes Date serialization in the PostgreSQL text-format bind path by calling toISOString() instead of String.fromJS() (which produced locale-dependent toString() output). It also fixes a real bug in the C++ bindings where JSC__JSValue__toISOString was writing to a local stack buffer instead of the caller-provided buffer, and adds JSValue.ISOStringBuffer as a shared named type to prevent future ABI drift.
Security risks
None. This is a data-serialization correctness fix confined to the PostgreSQL bind message path. No auth, crypto, or permissions code is touched.
Level of scrutiny
Moderate. The changes touch C++/Zig cross-language bindings (real ABI risk) and the PostgreSQL wire protocol serialization path (real correctness risk). However, the changes are focused and the PR went through multiple rounds of review where each issue was systematically addressed.
Other factors
All substantive issues I raised in prior rounds have been resolved: the ABI mismatch in getDateNowISOString was fixed, ISOStringBuffer was extracted as a named type, the afterAll Docker teardown was scoped correctly, and the prepare: true test now exercises both the text-format first-execution path and the binary types.date.fromJS second-execution path. The newly-reported NaN panic in date.zig:25 is pre-existing (that file is not part of this PR) and should be addressed in a follow-up.
|
@robobun resume. Fix the comments. Rebase |
Fixes #29010. Bun.SQL was sending JavaScript `Date` parameters through `String.fromJS(value)`, which yields `Date.prototype.toString()` output (`"Mon Jan 15 2024 12:30:45 GMT+0000 (Coordinated Universal Time)"`). PostgreSQL-compatible databases reject that with "invalid input syntax for type timestamp". The bug manifested whenever the parameter type tag was 0 ("server decides"), which happens under `prepare: false` and on the first execution of a fresh named prepared statement before Describe has returned. The binary-format `.timestamp` / `.timestamptz` path was already correct because it goes through `types.date.fromJS` → `getUnixTimestamp()`. Changes: - `PostgresRequest.zig`: in `writeBind`'s else branch, detect `value.isDate()` and emit the ISO 8601 / RFC 3339 string via `JSC__JSValue__toISOString`. On ISO serialization failure, propagate `error.InvalidQueryBinding` rather than falling back to the legacy `String.fromJS` path (which would re-emit the broken locale string). - `bindings.cpp`: fix a pre-existing bug in `JSC__JSValue__toISOString` where the helper wrote the ISO string into a local `char buffer[64]` and never copied it to the caller's output buffer. - `JSValue.zig`: widen the Zig-side `toISOString` / `getDateNowISOString` buffer from `*[28]u8` to `*[64]u8` to match `Bun::toISOString`'s contract, and fix a pre-existing ABI mismatch in the `getDateNowISOString` extern declaration (Zig declared `(*JSGlobalObject, f64) JSValue` but the C function is `(JSC::JSGlobalObject*, char*) int`). - `test/regression/issue/29010.test.ts`: regression test covering Date parameters in SELECT, INSERT via `sql(rows)`, INSERT as a plain parameter, non-UTC Date round-tripping as UTC, and the default `prepare: true` path. Resolves PostgreSQL via docker-compose or a local instance; does not tear down the shared compose project (it is a consumer, not the owner).
…cise binary path - JSValue.zig: export `pub const ISOStringBuffer = [64]u8` and use it for the `toISOString` / `getDateNowISOString` externs so the 64-byte contract with `Bun::toISOString` lives in one place. - PostgresRequest.zig: use `JSValue.ISOStringBuffer` at the call site. - bindings.cpp: collapse `JSC__JSValue__DateNowISOString` to a thin wrapper around `Bun::toISOString(vm, jsDateNow(), buf)`. This removes a 30-line copy of the formatter, the unnecessary `DateInstance` allocation, and makes null-termination consistent with `JSC__JSValue__toISOString`. - s3/credentials.zig: update the commented-out `getDateNowISOString` example to the new buffer type so uncommenting it would compile. - 29010.test.ts: run the `prepare: true` query twice so the second execution actually takes the binary `types.date.fromJS` path (first execution still has OID 0 and uses the ISO text path).
…oth paths
Invalid `Date` instances (`new Date(NaN)`, `new Date("bad")`) are
real `DateInstance`s whose internal value is NaN, so
`getUnixTimestamp()` — and likewise `parseDate` on a bad string or
`asNumber` on `NaN`/`Infinity` — can yield a non-finite double.
`@intFromFloat` on a non-finite value is Illegal Behavior: panic in
safe builds, silent UB in release.
The text path (`toISOString`) already rejects non-finite dates via
`std::isfinite()`; mirror that in the binary path by checking
`std.math.isFinite` before the cast and returning
`error.InvalidQueryBinding`. Widened the return type from
`bun.JSError!i64` to `AnyPostgresError!i64` to carry the new error;
the sole caller already uses an inferred error set.
Added two tests:
- `prepare: false` invalid Date → client-side
`ERR_POSTGRES_INVALID_QUERY_BINDING` instead of the old
`22007 invalid input syntax for "Invalid Date"` server error.
- `prepare: true` invalid Date on the primed binary path → same
error instead of panic / garbage timestamp.
Both tests call `.execute()` on the tagged-template query so
`expect().rejects` sees a real Promise rather than the lazy
`Query` thenable.
df43c24 to
d29f00c
Compare
`writeBind` (and the helpers that wrap it) append directly to `connection.write_buffer` and can now fail mid-message — `error.InvalidQueryBinding` for an invalid `Date` fires after the 'B' tag, a zeroed length placeholder, names, format codes and earlier parameter values are already in the buffer. The catch blocks in `advance()` / `doRun` rejected the query but never truncated `write_buffer`, so a user who caught the rejection and issued another query on the same connection had its frames appended after the partial Bind, and `flushData()` shipped a protocol stream PostgreSQL rejected with "invalid message length", dropping the connection. Add `WriteBufferSnapshot` (capture `write_buffer.byte_list.len` before a `PostgresRequest.*` call, restore it at the top of the `catch`) and apply it at every call site in `advance()` and `doRun`. This also hardens the pre-existing mid-write error paths (`iter.anyFailed()`, throwing `[Symbol.toPrimitive]`, circular JSON in `jsonStringify`) that previously required exotic input. Add two connection-survival tests that issue a `SELECT 1` after a caught invalid-Date bind error on both the text (`prepare: false`) and binary (`prepare: true`, primed statement) paths. Without the rollback both fail with `ERR_POSTGRES_CONNECTION_CLOSED`.
Follow-up to f68c8e6 for completeness. Three adjacent catch blocks weren't covered by `WriteBufferSnapshot`: - `advance()`'s `connection_writer.write(&protocol.Sync)` catch after the snapshotted `writeQuery` — `write_start` is already in scope, just call `.restore(this)`. - `doRun`'s parallel `writer.write(&protocol.Sync)` catch — same. - `doRun`'s simple-query `PostgresRequest.executeQuery` — had no snapshot at all while the identical call in `advance()` did. All three are only reachable on allocator OOM (no user-data path to trigger them), so this is consistency with the other instrumented sites rather than a live bug.
There was a problem hiding this comment.
All prior review threads are resolved and I didn't find anything new, but the scope has grown well beyond the original serialization fix — it now adds a WriteBufferSnapshot rollback mechanism across ~10 call sites in the Postgres connection state machine and reworks the toISOString/DateNowISOString Zig↔C++ ABI, so it's worth a human pass before merging.
Extended reasoning...
Overview
This PR started as a targeted fix for #29010 (serialize JS Date as ISO 8601 in the Postgres text-format bind path) but accreted several related fixes through review:
PostgresRequest.zig:writeBindelsebranch now detectsvalue.isDate()and emitstoISOString()instead of falling through toString.fromJS→ localetoString(). Returnserror.InvalidQueryBindingfor non-finite dates.bindings.cpp/JSValue.zig: Fixed a pre-existing bug whereJSC__JSValue__toISOStringwrote into a localchar buffer[64]and discarded the caller's buffer; fixed theJSC__JSValue__DateNowISOStringZig extern ABI mismatch (f64→pointer,JSValue→c_int); collapsed its body to delegate toBun::toISOString; introducedJSValue.ISOStringBuffer = [64]u8.types/date.zig: Addedstd.math.isFiniteguard before@intFromFloatto avoid Zig Illegal Behavior onnew Date(NaN)via the binary path; widened return type toAnyPostgresError!i64.PostgresSQLConnection.zig/PostgresSQLQuery.zig: NewWriteBufferSnapshotstruct that captureswrite_buffer.byte_list.lenbefore eachPostgresRequest.*call and truncates back in thecatchblock, so a mid-write bind error doesn't leave a partial frame that poisons the connection. Applied at ~10 call sites acrossadvance()anddoRun.s3/credentials.zig: Comment-only update to reference the new buffer type.test/regression/issue/29010.test.ts: 9 tests covering text/binary paths, invalid dates, and connection survival after a caught bind error.
Security risks
None identified. Parameters in the Postgres Bind message are length-prefixed, so the serialization change carries no injection risk. WriteBufferSnapshot.restore only truncates an in-memory buffer length (with debugAssert invariants). The FFI signature changes were verified against the C++ side.
Level of scrutiny
This warrants a human review. While each piece is well-reasoned and every prior review thread is resolved, the PR now touches:
- Zig↔C++ FFI signatures (ABI-sensitive),
- the Postgres wire-protocol writer's error-recovery semantics (a new rollback abstraction threaded through the connection state machine and query enqueue path),
- an error-set widening (
bun.JSError!i64→AnyPostgresError!i64) on a shared helper.
The WriteBufferSnapshot addition in particular is a design-level change to connection error recovery that a maintainer should sign off on — it's correct as far as I can tell, and the connection-survival tests gate it, but it's not the kind of mechanical change I should approve unilaterally.
Other factors
- All 11 prior inline review threads (mine and CodeRabbit's) are marked resolved, with the most recent commit (389dd7c) addressing the last consistency nit.
- CI shows 1 failure in
test/bake/dev-and-prod.test.tson Windows, which is unrelated to this change. - Two maintainers (alii, Jarred-Sumner) have engaged on the thread but only with directives to the bot; neither has reviewed the final state.
Fixes #29010.
Repro
Cause
In
PostgresRequest.zig#writeBind, theswitch (tag)over theparameter type only has binary-format cases for
.timestamp/.timestamptz. Any other tag — including0("server decides",which is what
Signature.generatewrites for dates so Postgrespicks the type, and what any
prepare: falsequery sends) — fallsthrough to:
String.fromJSon a JSDatetriggers the ordinary JStoString()conversion, which for dates produces a locale /timezone-dependent string that PostgreSQL cannot parse.
The prepared-statement binary path was already correct because
types.date.fromJSusesvalue.getUnixTimestamp().Fix
In the
elsebranch ofwriteBind, detectvalue.isDate()andemit the ISO 8601 / RFC 3339 text representation instead of
falling into
String.fromJS. This matches bothpostgres.js(
types.date.serializehardcodes.toISOString()) andpg(builds an ISO 8601 string).
Fix a pre-existing bug in
JSC__JSValue__toISOStringinbindings.cpp: it was writing to a localchar buffer[64]anddiscarding the caller's output buffer entirely. Now writes into
buf, which makes the ZigJSValue.toISOStringhelper actuallyreturn a populated slice. Widened the Zig-side buffer from
*[28]u8to*[64]u8to matchBun::toISOString's contract.Verification
test/regression/issue/29010.test.tscovers:SELECTparameter cast to::timestamptzINSERTviasql([{ ... }])row helperINSERTas a plain parameterprepare: true) binary path still worksWithout the fix, the four
prepare: falsetests fail withERR_POSTGRES_SERVER_ERROR 22007 invalid input syntax for type timestamp with time zone. With the fix, all five pass.