Summary
FieldValueRequest cannot tag a scalar value with its CloudKit type, so writing a Date/Time (TIMESTAMP) field is impossible. The .date FieldValue is serialized as a bare JSON number with no type, CloudKit infers INT64 from the number, and rejects it against the TIMESTAMP schema field with BAD_REQUEST.
This blocks CelestraCloud (and will affect BushelCloud): every record write that includes a timestamp field fails.
The issue CelestraCloud is having
CelestraCloud's schema declares timestamp fields as TIMESTAMP:
Feed: verifiedTimestamp, attemptedTimestamp
Article: fetchedTimestamp, expiresTimestamp, publishedTimestamp
The conversion code correctly emits FieldValue.date(...) for them. Every create/update then fails:
Batch 1 failed: CloudKit record operation failed for 'EB19FB9C-...' (BAD_REQUEST)
Reason: Invalid value, expected type TIMESTAMP.
...
Batch complete: 0/7 (0.0%)
Failing CI runs (CelestraCloud @ dbdba1a, MistKit v1.0.0-beta.2):
Note on the companion build failure (https://github.com/brightdigit/CelestraCloud/actions/runs/26305885729): CelestraError.swift referenced CloudKitError.zonePaginationLimitExceeded / .conversionFailed / .recordOperationFailed. Those cases exist in the current v1.0.0-beta.2 HEAD and a fresh build now passes — that run failed only because the v1.0.0-beta.2 branch pointer hadn't yet advanced to include the Zone API commits (#367/#368) when it ran. It resolves on re-run; no code change required. The timestamp write failure is the real, unfixed defect.
Root cause
A CloudKit Web Services field is { value: CKValue, type: string (optional) }. type is optional in general, but a Date/Time value is just a number on the wire — indistinguishable from INT64/DOUBLE. Without type: "TIMESTAMP", CloudKit infers INT64 and rejects it against a TIMESTAMP schema field. (Apple docs: Types and Dictionaries; matching error + fix in forum thread 76351, where the resolution is { "type": "TIMESTAMP", "value": 1493382919000 }.)
MistKit currently can't send that tag. The request type enum only contains the *_LIST filter types — no scalar TIMESTAMP:
# openapi.yaml:1027 (FieldValueRequest.type)
enum: [STRING_LIST, INT64_LIST, DOUBLE_LIST, BYTES_LIST, TIMESTAMP_LIST, REFERENCE_LIST, LOCATION_LIST, ASSET_LIST, LIST]
…and the serializer emits a bare DateValue (a Double) with no type:
// Sources/MistKit/OpenAPI/Components/Components.Schemas.FieldValueRequest.swift:111
if case .date(let value) = fieldValue {
return Self(value: .DateValue(value.timeIntervalSince1970 * 1_000)) // no _type
}
The asymmetry: reads work because CloudKit supplies the type in responses (the response enum does include scalar TIMESTAMP, openapi.yaml:1051) and we use it to recover .date vs .double:
// Sources/MistKit/Models/FieldValues/FieldValue+Components.swift:132
if case .DoubleValue(let dblVal) = value {
return fieldType == .TIMESTAMP
? .date(Date(timeIntervalSince1970: dblVal / 1_000))
: .double(dblVal)
}
So the information exists in the domain model (.date is a distinct case) — the request path just drops it.
Proposed work
- Explain / reproduce the CelestraCloud failure above (timestamp writes →
BAD_REQUEST). ✅ documented here.
- Add the missing scalar types to
FieldValueRequest.type (openapi.yaml:1008), regenerate MistKitOpenAPI (Types.swift:952 _typePayload), and emit type when serializing the corresponding FieldValue. At minimum add TIMESTAMP and set it for .date. Investigate whether type can be made required on requests.
- If
type can't be required (CloudKit documents it as optional, so a global requirement is likely wrong/over-restrictive), then enumerate and document exactly which types do require an explicit tag because their JSON value is otherwise ambiguous, and emit type only for those. Candidates:
TIMESTAMP — number, otherwise read as INT64/DOUBLE ← the blocker
BYTES — base64 string, otherwise read as STRING
DOUBLE — whole-valued doubles can serialize without a fraction and be read as INT64
- (object/array-shaped types —
REFERENCE, ASSET, LOCATION, LIST — are unambiguous and should stay untagged)
Priority
Unblocking CelestraCloud is the goal — the timestamp write failure (and confirming the build failure clears on re-run). The minimal fix is to make .date writes carry type: "TIMESTAMP"; the broader ambiguous-type audit (item 3) can follow.
Summary
FieldValueRequestcannot tag a scalar value with its CloudKit type, so writing a Date/Time (TIMESTAMP) field is impossible. The.dateFieldValueis serialized as a bare JSON number with notype, CloudKit infersINT64from the number, and rejects it against theTIMESTAMPschema field withBAD_REQUEST.This blocks CelestraCloud (and will affect BushelCloud): every record write that includes a timestamp field fails.
The issue CelestraCloud is having
CelestraCloud's schema declares timestamp fields as
TIMESTAMP:Feed:verifiedTimestamp,attemptedTimestampArticle:fetchedTimestamp,expiresTimestamp,publishedTimestampThe conversion code correctly emits
FieldValue.date(...)for them. Every create/update then fails:Failing CI runs (CelestraCloud @
dbdba1a, MistKitv1.0.0-beta.2):Root cause
A CloudKit Web Services field is
{ value: CKValue, type: string (optional) }.typeis optional in general, but a Date/Time value is just a number on the wire — indistinguishable fromINT64/DOUBLE. Withouttype: "TIMESTAMP", CloudKit infersINT64and rejects it against aTIMESTAMPschema field. (Apple docs: Types and Dictionaries; matching error + fix in forum thread 76351, where the resolution is{ "type": "TIMESTAMP", "value": 1493382919000 }.)MistKit currently can't send that tag. The request
typeenum only contains the*_LISTfilter types — no scalarTIMESTAMP:…and the serializer emits a bare
DateValue(aDouble) with no type:The asymmetry: reads work because CloudKit supplies the type in responses (the response enum does include scalar
TIMESTAMP,openapi.yaml:1051) and we use it to recover.datevs.double:So the information exists in the domain model (
.dateis a distinct case) — the request path just drops it.Proposed work
BAD_REQUEST). ✅ documented here.FieldValueRequest.type(openapi.yaml:1008), regenerateMistKitOpenAPI(Types.swift:952_typePayload), and emittypewhen serializing the correspondingFieldValue. At minimum addTIMESTAMPand set it for.date. Investigate whethertypecan be made required on requests.typecan't be required (CloudKit documents it as optional, so a global requirement is likely wrong/over-restrictive), then enumerate and document exactly which types do require an explicit tag because their JSON value is otherwise ambiguous, and emittypeonly for those. Candidates:TIMESTAMP— number, otherwise read asINT64/DOUBLE← the blockerBYTES— base64 string, otherwise read asSTRINGDOUBLE— whole-valued doubles can serialize without a fraction and be read asINT64REFERENCE,ASSET,LOCATION,LIST— are unambiguous and should stay untagged)Priority
Unblocking CelestraCloud is the goal — the timestamp write failure (and confirming the build failure clears on re-run). The minimal fix is to make
.datewrites carrytype: "TIMESTAMP"; the broader ambiguous-type audit (item 3) can follow.