{"record":{"id":"0da23a5e9ea080f2","repo":"slackhq/nebula","slug":"noise-writemessage-w","errorCode":null,"errorMessage":"noise WriteMessage: %w","messagePattern":"noise WriteMessage: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"handshake/machine.go","lineNumber":450,"sourceCode":"\theader.Encode(\n\t\tout[start:],\n\t\theader.Version, header.Handshake, m.subtype,\n\t\tm.result.RemoteIndex,\n\t\tuint64(m.hs.MessageIndex()+1),\n\t)\n\n\t// noise.WriteMessage appends the encrypted handshake message to out,\n\t// reusing capacity when present.\n\t//\n\t// The (dKey, eKey) ordering here is correct for IX, where the responder\n\t// completes the handshake by writing the stage-2 message. noise returns\n\t// (cs1, cs2) where cs1 is the initiator->responder cipher (which is the\n\t// responder's decrypt key). For 3-message patterns where an initiator\n\t// finishes by writing the final message, this ordering would be wrong;\n\t// revisit when XX/pqIX lands.\n\tout, dKey, eKey, err := m.hs.WriteMessage(out, hsBytes)\n\tif err != nil {\n\t\treturn nil, nil, nil, fmt.Errorf(\"noise WriteMessage: %w\", err)\n\t}\n\n\treturn out, dKey, eKey, nil\n}\n","sourceCodeStart":432,"sourceCodeEnd":455,"githubUrl":"https://github.com/slackhq/nebula/blob/dd8f660c0ac37903ec4080ca4d3c861ba9342ceb/handshake/machine.go#L432-L455","documentation":"Wraps an error from noiseprotocol's WriteMessage during handshake response construction. WriteMessage encrypts the handshake payload and derives the data-cipher keys; a failure means the Noise state machine rejected the step (bad state, message too large, or internal crypto failure).","triggerScenarios":"Calling buildResponse (from Initiate or ProcessPacket) when m.hs.WriteMessage fails because the handshake state is not at the expected message index, hsBytes exceeds Noise max message size, or the cipher state is invalid.","commonSituations":"Replaying/corrupting handshake state after a failed earlier step; concurrently calling Initiate/ProcessPacket on the same machine; oversized payload; noise library version mismatch.","solutions":["Discard the handshake machine and start a fresh Initiate/handshake from state 0","Check that ProcessPacket/Initiate are not called concurrently on the same machine (serialize with a lock)","Inspect the wrapped noise error for message-index/state mismatch and ensure messages are processed in order","Upgrade nebula/noise library if the pattern requires more message space (see comment about 3-message patterns)"],"exampleFix":"// before\nout, dKey, eKey, err := m.hs.WriteMessage(out, hsBytes)\n// after: recover by resetting the handshake\nout, dKey, eKey, err := m.hs.WriteMessage(out, hsBytes)\nif err != nil {\n    // caller should rebuild: m = NewMachine(...) and retry handshake\n    return nil, nil, nil, fmt.Errorf(\"noise WriteMessage: %w\", err)\n}","handlingStrategy":"retry","validationCode":"if m.hs == nil {\n    return errors.New(\"handshake state missing; re-initiate\")\n}","typeGuard":null,"tryCatchPattern":"out, dKey, eKey, err := buildResponse(...)\nif err != nil {\n    // reset handshake state and retry once\n    m = NewMachine(subtype, ...)\n    out, dKey, eKey, err = buildResponse(...)\n    if err != nil { return fmt.Errorf(\"handshake failed after retry: %w\", err) }\n}","preventionTips":["Never share a handshake Machine across goroutines without locking","Process handshake messages strictly in order","Discard and rebuild machines after any noise error instead of reusing them"],"tags":["noise","handshake","crypto"],"backgroundTag":"noise-handshake-failure","analyzedSha":"dd8f660c0ac37903ec4080ca4d3c861ba9342ceb","analyzedAt":"2026-09-03T11:13:55.444Z","contentChangedAt":"2026-09-03T11:13:55.444Z","schemaVersion":2},"datasetVersion":"2026-09-10T17:17:09.494Z"}