Summary
Writes to fields declared readonly: true are silently dropped — including writes issued by the platform's own automation engine (flow update_record / create_record nodes). The only signal is a WARN in the server log (Field 'X' is read-only — ignoring incoming change (#2948)); the flow step still reports status: "success" and the run completes "successfully" while the intended data change never happened.
Environment
@objectstack/* 16.1.0 (open edition), single-tenant, sqlite-wasm driver
Steps to reproduce
- Declare a field
readonly: true (e.g. crm_opportunity.approval_status, crm_lead.converted_account, crm_case.is_sla_violated).
- Have a flow write it via an
update_record node (e.g. the approval flow stamping approval_status: 'approved', or a lead-conversion flow stamping converted_account).
- Run the flow and inspect the flow run + the target record.
Actual behaviour
- Server logs:
WARN Field 'approval_status' is read-only — ignoring incoming change (#2948).
- The flow run’s step shows
{"nodeId":"mark_converted","status":"success"} — no error, no partial-failure marker.
- The field retains its old value. Downstream logic that depends on it (approval entry conditions, "converted" list filters, SLA violation reports) silently misbehaves.
Expected behaviour
Pick one and make it consistent + documented:
- (preferred)
readonly should govern the external API / UI surface only, and not block trusted server-side writers (flow engine, hooks running as system, seeds). This matches the mental model most apps have — "users can't edit this, but automation maintains it."
- OR, if
readonly is meant to be absolute, a flow update_record that targets a readonly field should fail the node (surface an error), not silently succeed — so authors discover the mismatch at design time instead of shipping a no-op automation.
Either way, a dropped write should never be reported as a successful step.
Related: flow step reports success on failure
Separately observed: a create_record node that actually failed (crm_contact is required) still surfaced its error object stringified as "[object Object]" in one code path, and the enclosing run summary’s per-step status wasn’t always a reliable indicator of the node outcome. Step status should be authoritative and errors should serialize to a readable message.
Impact
High. This turns whole categories of automation (approval status tracking, lead-conversion stamping, SLA flags, campaign rollups) into silent no-ops. Because validate/build can’t see it and the run reports success, it only surfaces by manually reading server WARN logs or noticing wrong data days later.
Workaround
Remove readonly from any field an automation writes and enforce edit-protection with a validation rule instead (record.is_converted == true && record.x != previous.x → error). Works, but loses the field-level UI affordance and is easy to forget.
Summary
Writes to fields declared
readonly: trueare silently dropped — including writes issued by the platform's own automation engine (flowupdate_record/create_recordnodes). The only signal is aWARNin the server log (Field 'X' is read-only — ignoring incoming change (#2948)); the flow step still reportsstatus: "success"and the run completes "successfully" while the intended data change never happened.Environment
@objectstack/*16.1.0 (open edition), single-tenant, sqlite-wasm driverSteps to reproduce
readonly: true(e.g.crm_opportunity.approval_status,crm_lead.converted_account,crm_case.is_sla_violated).update_recordnode (e.g. the approval flow stampingapproval_status: 'approved', or a lead-conversion flow stampingconverted_account).Actual behaviour
WARN Field 'approval_status' is read-only — ignoring incoming change (#2948).{"nodeId":"mark_converted","status":"success"}— no error, no partial-failure marker.Expected behaviour
Pick one and make it consistent + documented:
readonlyshould govern the external API / UI surface only, and not block trusted server-side writers (flow engine, hooks running as system, seeds). This matches the mental model most apps have — "users can't edit this, but automation maintains it."readonlyis meant to be absolute, a flowupdate_recordthat targets a readonly field should fail the node (surface an error), not silently succeed — so authors discover the mismatch at design time instead of shipping a no-op automation.Either way, a dropped write should never be reported as a successful step.
Related: flow step reports
successon failureSeparately observed: a
create_recordnode that actually failed (crm_contact is required) still surfaced its error object stringified as"[object Object]"in one code path, and the enclosing run summary’s per-step status wasn’t always a reliable indicator of the node outcome. Step status should be authoritative and errors should serialize to a readable message.Impact
High. This turns whole categories of automation (approval status tracking, lead-conversion stamping, SLA flags, campaign rollups) into silent no-ops. Because
validate/buildcan’t see it and the run reports success, it only surfaces by manually reading server WARN logs or noticing wrong data days later.Workaround
Remove
readonlyfrom any field an automation writes and enforce edit-protection with a validation rule instead (record.is_converted == true && record.x != previous.x→ error). Works, but loses the field-level UI affordance and is easy to forget.