filesoft.Discuss a project
← Practical FileMaker guides

PRACTICAL FILEMAKER GUIDES

Handle FileMaker error 301 without losing the user’s intended change

Use a two-session practice case to recognize a record lock, preserve pending input and retry only after reviewing the current record.

1. Identify the failed operation

Error 301 means a record is in use by another user. It describes a conflict at an operation, not proof that your entire script failed or that the record was deleted. Save the error immediately after the step that can fail, before another step replaces it.

This intermediate exercise needs an owner-controlled hosted practice file and two independent FileMaker Pro sessions. Use fictional data and existing authorized test accounts. Two windows can demonstrate some locking behavior, but they are not a substitute for the two-client test described here. No hosting configuration is part of this article.

Give the practice Requests table RequestID and Status as Text fields. Add R001 with Status “Draft” and R002 with “Draft.” Put the ID beside the status so both reviewers can confirm which record they are using. The aim is to preserve a proposed change without silently overwriting someone else’s work.

2. Create one deliberate conflict

SessionAction
AOpen R001 for editing; change Status to In review; leave uncommitted
BRecord proposed Status Ready separately; attempt to open R001 for editing
BCapture the lock failure; do not write or claim success
ACommit In review; confirm success
BReview current In review before deciding whether Ready is still appropriate

Keep B’s proposed value in a script variable or a separate draft-entry interface, not by first changing the record that is contested. Also retain the intended RequestID and the original status B had reviewed. A plain worksheet is enough to rehearse the sequence before implementing the interface.

In session B, a worker can use Open Record/Request and immediately capture Get ( LastError ). Claris documents that opening checks access and attempts a write lock; failure can also involve another window or insufficient privileges. Handle the actual code instead of labeling every failure “another user.”

Download the two-session checklist ↓

3. Return a useful result

Define outcomes such as saved, conflict, cancelled and error. The calling interface can tell B, “This request is being edited. Your proposed status has not been saved.” Keep the proposed value available while the user decides what to do.

For this exercise, cancellation means abandoning B’s proposal. It must not revert A’s changes or close A’s session. A permission failure calls for an access review; a missing record calls for a missing-record response. Neither is solved by repeatedly trying a write.

Log only what helps diagnosis: operation label, saved error and a suitable record reference. Do not copy full record contents into a log merely to diagnose contention. A dialog disappearing does not prove that a record was committed.

4. Reread under the lock before writing

A successful later lock does not mean B’s old information is current. For each explicit retry, locate the intended ID, acquire the lock, then reread the stored values and compare them with the state B last accepted. Stop on any failed navigation or lock operation.

If the record changed, release only the lock acquired by this operation without making a write, show the current and proposed values, and ask the user to review the difference. Do not keep a lock while the user spends time reading a dialog. After that review, a new attempt must acquire the lock and compare again: another client may have changed the record in the meantime.

Preserve intended ID, reviewed state and proposed value.
Locate intended record; stop if navigation fails.
Acquire write lock; capture result immediately.
If locked elsewhere: return conflict, with no write.
Reread current state while holding the lock.
If state differs: release this operation's lock; return changed.
Apply the reviewed proposal; check the write.
Commit; check the commit before returning saved.

This is an algorithm to implement and test, not pasteable FileMaker script syntax. It assumes the operation starts without unrelated uncommitted edits. Do not use a blanket revert that could discard a user’s earlier work.

5. Check recovery as carefully as success

TestExpected contract
A holds R001B reports conflict; no B write
B cancelsA’s stored record remains intact
A commits In reviewB detects changed state before proposing another write
Record changes againB must review again; no stale overwrite
B writes R002Unrelated R001 lock is not treated as a global failure
Validation rejects BNo saved result; recovery stays explicit

Inspect values in both sessions after each case. Add a read-only-account case using an existing test account. Record the actual error instead of assuming it will be 301. Test commit failure separately from lock acquisition.

If failure leaves an edit pending, the implementation must offer correction or deliberately discard its own change. Do not report success from the lock result alone. Capture the commit result, return it to the caller and verify the stored record after reopening.

6. Keep contention handling bounded

Use an explicit retry action for this small workflow. A tight loop can repeatedly hit the same lock while leaving the user unsure what happened. Automated background retries need a separate limit, delay and duplicate-operation policy.

A transaction can group related writes, but it does not remove contention or undo an external email. Do not promise multi-user support for a Filesoft product merely because this generic learning example uses two sessions.

Finished result: a conflict is visible, pending input is preserved, and a later save uses a newly reviewed record state. Record client and server versions, accounts, actual errors and outcomes. The checklist is a native review plan, not evidence that the exercise has already run.

FileMaker script example

This example requires FileMaker Pro 19.6.1 or later and no active transaction.

Create a Requests layout on Requests. Use the R001/R002 records above, unique RequestIDs, no script triggers, and no unrelated pending edits. Run this worker in session B; it leaves the Requests layout and found set active. FileMaker Pro 19.6.1 or later is the baseline for these practice examples.

Pass a JSON object such as {"requestId":"R001","reviewedStatus":"Draft","proposedStatus":"Ready"}. The restricted IDs keep this tiny fixture’s exact find unambiguous. For a production identifier, separately handle FileMaker find operators and permissions. A caller retains the proposal and calls this worker once per explicit user retry.

How to use this: recreate the steps and their options in Script Workspace. This is readable script text, not a clipboard snippet or an importable script. These examples have been checked against documentation but have not been executed in a native FileMaker file.

Recreate these steps

FileMaker script

# Requests | Try reviewed status once
Set Error Capture [ On ]
If [ Get ( WindowMode ) ≠ 0 or Get ( RecordOpenState ) ≠ 0 or Get ( TransactionOpenState ) ]
    Exit Script [ Text Result: "blocked: finish the current edit" ]
End If
Set Variable [ $p ; Value: Get ( ScriptParameter ) ]
If [ JSONGetElementType ( $p ; "" ) ≠ JSONObject ]
    Exit Script [ Text Result: "invalid_parameter" ]
End If
If [ JSONGetElementType ( $p ; "requestId" ) ≠ JSONString or JSONGetElementType ( $p ; "reviewedStatus" ) ≠ JSONString or JSONGetElementType ( $p ; "proposedStatus" ) ≠ JSONString ]
    Exit Script [ Text Result: "invalid_parameter" ]
End If
Set Variable [ $id ; Value: JSONGetElement ( $p ; "requestId" ) ]
Set Variable [ $reviewed ; Value: JSONGetElement ( $p ; "reviewedStatus" ) ]
Set Variable [ $proposed ; Value: JSONGetElement ( $p ; "proposedStatus" ) ]
If [ ( $id ≠ "R001" and $id ≠ "R002" ) or IsEmpty ( Trim ( $proposed ) ) ]
    Exit Script [ Text Result: "invalid_parameter" ]
End If
Loop
    Go to Layout [ "Requests" (Requests) ; Animation: None ]
    Set Variable [ $error ; Value: Get ( LastError ) ]
    Exit Loop If [ $error ≠ 0 ]
    Enter Find Mode [ Pause: Off ]
    Set Variable [ $error ; Value: Get ( LastError ) ]
    Exit Loop If [ $error ≠ 0 ]
    Set Field [ Requests::RequestID ; "==" & $id ]
    Set Variable [ $error ; Value: Get ( LastError ) ]
    Exit Loop If [ $error ≠ 0 ]
    Perform Find [ ]
    Set Variable [ $error ; Value: Get ( LastError ) ]
    Exit Loop If [ $error ≠ 0 ]
    Exit Loop If [ 1 ]
End Loop
If [ $error = 401 ]
    Exit Script [ Text Result: "not_found" ]
Else If [ $error ≠ 0 ]
    Exit Script [ Text Result: "error:" & $error ]
Else If [ Get ( FoundCount ) ≠ 1 ]
    Exit Script [ Text Result: "ambiguous_id" ]
End If
Open Record/Request
Set Variable [ $error ; Value: Get ( LastError ) ]
If [ $error = 301 ]
    Exit Script [ Text Result: "conflict" ]
Else If [ $error ≠ 0 ]
    Exit Script [ Text Result: "error:" & $error ]
End If
# The lock succeeded; compare again before writing.
Set Variable [ $current ; Value: Requests::Status ]
If [ Requests::RequestID ≠ $id or not Exact ( $current ; $reviewed ) ]
    Revert Record/Request [ With dialog: Off ]
    Set Variable [ $cleanupError ; Value: Get ( LastError ) ]
    If [ $cleanupError ≠ 0 ]
        Exit Script [ Text Result: "cleanup_error:" & $cleanupError ]
    End If
    Exit Script [ Text Result: "changed:" & $current ]
End If
Set Field [ Requests::Status ; $proposed ]
Set Variable [ $error ; Value: Get ( LastError ) ]
If [ $error = 0 ]
    Commit Records/Requests [ With dialog: Off ]
    Set Variable [ $error ; Value: Get ( LastError ) ]
End If
If [ $error ≠ 0 ]
    Revert Record/Request [ With dialog: Off ]
    Set Variable [ $cleanupError ; Value: Get ( LastError ) ]
    Exit Script [ Text Result: "error:" & $error & ";cleanup:" & $cleanupError ]
End If
Exit Script [ Text Result: "saved" ]

In the caller, read Get ( ScriptResult ) immediately. For conflict, preserve the proposal and offer Cancel or Retry; do not loop automatically. For changed:, show the returned current value and the proposal after the worker has released its lock. Only a new user review should replace reviewedStatus before another call. A cleanup error needs attention before another attempt.

Test with A holding R001, then A committing “In review”: B should first get conflict, then changed:In review, never a silent overwrite. A subsequent explicitly reviewed call may return saved. Use a separate privilege test and a commit-validation failure; do not treat every error as 301.

References

Use a practice copy and verify the result in your FileMaker version. Product requirements and platform support remain specific to each product.

Filesoft counts page loads and selected resource and practice-download clicks in aggregate. These aggregate counts contain no practice data or visitor identifiers. Browser privacy signals and the site’s analytics-off preference disable these counts.