Error codes · Declarations and their clauses
PW0338
sent_without_idempotency
The program must hold to this: a command a page's handler calls declares idempotent_by.
A program the compiler refuses with it
module twice
import context.{ current_session }
import capability.{ Session, SessionId }
type Post = Post { id: String, text: String }
type PostError =
| Unavailable
fn publish(s: Session<SessionId>, text: String) -> Result<Post, PostError> !{ database.write<Post> }
host "blog:data/posts#publish"
command post(text: String) -> Result<Post, PostError> {
publish(current_session(), text)
}
page Write() {
route "/write"
signal sent: Int = 0
view {
<title>Write</title>
<button type="button" on:press={() => match post("Hello") {
Ok(_) => sent = sent + 1,
Err(_) => (),
}}>Post</button>
}
}error: [PW0338] `post` is called by a page's handler, and declares no `idempotent_by`
--> twice.pw:14:1
|
14 | / command post(text: String) -> Result<Post, PostError> {
15 | | publish(current_session(), text)
16 | | }
| |_^ `post` is called by a page's handler, and declares no `idempotent_by`
...
25 | <button type="button" on:press={() => match post("Hello") {
| ------------- a press sends it from here
|
= note: A browser's request can reach the server twice: a retrying proxy, a flaky network or a client's own retry sends it again after the first arrived (RFC 9110 lets a client retry only a request it knows to be idempotent). Each press carries an interaction, and `idempotent_by InteractionId` runs the command once for it (ADR-0121).
= help: declare `idempotent_by InteractionId`From A command a page can send twice, in Errors and diagnostics. A test holds the guide's examples to the compiler.
The decisions behind it
Reading the diagnostic
pw check prints the code in brackets, [PW0338], with the sentence above and the place in your program. Errors and diagnostics explains how to read one, and every code is listed with its rule.