{"record":{"id":"b1f34655bc7e721b","repo":"neondatabase/neon","slug":"exceptionalcondition-s-d-s","errorCode":null,"errorMessage":"ExceptionalCondition: %s:%d: %s\n","messagePattern":"ExceptionalCondition: (.+?):(.+?): (.+?)\n","errorType":"console","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"pgxn/neon/walproposer_compat.c","lineNumber":19,"sourceCode":"/*\n * Contains copied/adapted functions from libpq and some internal postgres functions.\n * This is needed to avoid linking to full postgres server installation. This file\n * is compiled as a part of libwalproposer static library.\n */\n#include \"postgres.h\"\n\n#include <stdio.h>\n\n#include \"libpq/pqformat.h\"\n#include \"miscadmin.h\"\n#include \"utils/datetime.h\"\n#include \"walproposer.h\"\n\nvoid\nExceptionalCondition(const char *conditionName,\n\t\t\t\t\t const char *fileName, int lineNumber)\n{\n\tfprintf(stderr, \"ExceptionalCondition: %s:%d: %s\\n\",\n\t\t\tfileName, lineNumber, conditionName);\n\tfprintf(stderr, \"aborting...\\n\");\n\texit(1);\n}\n\nvoid\npq_copymsgbytes(StringInfo msg, char *buf, int datalen)\n{\n\tif (datalen < 0 || datalen > (msg->len - msg->cursor))\n\t\tExceptionalCondition(\"insufficient data left in message\", __FILE__, __LINE__);\n\tmemcpy(buf, &msg->data[msg->cursor], datalen);\n\tmsg->cursor += datalen;\n}\n\n/* --------------------------------\n *\t\tpq_getmsgint\t- get a binary integer from a message buffer\n *\n *\t\tValues are treated as unsigned.","sourceCodeStart":1,"sourceCodeEnd":37,"githubUrl":"https://github.com/neondatabase/neon/blob/8f60b04da47ffefe0e52bda2440134b42874eb75/pgxn/neon/walproposer_compat.c#L1-L37","documentation":"walproposer_compat.c reimplements Postgres's ExceptionalCondition for the standalone walproposer used by safekeepers. When an internal invariant fails, for example pq_copymsgbytes finding fewer bytes in the message than requested, it prints the failing file, line, and condition name, then terminates the process with exit(1). There is no unwinding or recovery path.","triggerScenarios":"Any assertion inside the walproposer compat layer: pq_copymsgbytes with datalen exceeding the remaining message, pq_getmsgint with an unsupported width, or any other failed check compiled with this handler.","commonSituations":"Protocol or version mismatch between the compute's walproposer and the safekeeper producing messages the parser rejects; truncated or corrupted frames on the wire; framing bugs after protocol changes.","solutions":["Read the preceding line 'ExceptionalCondition: file:line: condition'; it names the exact failed check","Verify the compute (neon extension) and safekeeper run compatible neon versions","Inspect safekeeper logs around the crash and enable walproposer logging to capture the offending message","If the input was well-formed, preserve logs and report the crash; exit(1) on valid input is a bug"],"exampleFix":"/* before: parser asserts (and exits) on truncated input */\npq_copymsgbytes(msg, buf, datalen);\n/* after: validate remaining length and reject the message cleanly */\nif (datalen < 0 || datalen > (msg->len - msg->cursor)) {\n    elog(ERROR, \"malformed message: need %d bytes, have %zu\",\n         datalen, msg->len - msg->cursor);\n}\npq_copymsgbytes(msg, buf, datalen);","handlingStrategy":"validation","validationCode":"/* check before every parse: never let the compat layer assert */\nstatic bool\nenough_bytes(StringInfo msg, int need)\n{\n    return need >= 0 && need <= (msg->len - msg->cursor);\n}\n\nif (!enough_bytes(msg, datalen))\n    return -1; /* reject the frame instead of exiting */","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Version-lock compute and safekeeper neon builds","Validate frame lengths before pq_* parse calls in custom protocol code","Run safekeepers under a supervisor that restarts on exit while the cause is investigated","Keep walproposer logging enabled to capture the failing message"],"tags":["walproposer","safekeeper","assertion","crash","c","protocol"],"backgroundTag":"assertion-failure","analyzedSha":"8f60b04da47ffefe0e52bda2440134b42874eb75","analyzedAt":"2026-08-16T23:39:28.135Z","schemaVersion":2},"datasetVersion":"2026-08-17T04:17:16.089Z"}