{"record":{"id":"30f96abc52862cc7","repo":"hasura/graphql-engine","slug":"user-error-from-plugin-plugin-name-30f96a","errorCode":null,"errorMessage":"User error from plugin {plugin_name}","messagePattern":"User error from plugin (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"v3/crates/plugins/pre-ndc-response-plugin/src/execute.rs","lineNumber":31,"sourceCode":"\n#[derive(Debug, thiserror::Error)]\npub enum Error {\n    #[error(\"Error while making the HTTP request to the pre-response plugin {0} - {1}\")]\n    ErrorWhileMakingHTTPRequestToTheHook(String, reqwest::Error),\n    #[error(\"Error while building the request for the pre-response plugin {0} - {1}\")]\n    BuildRequestError(String, #[source] BuildRequestError),\n    #[error(\"Reqwest error: {0}\")]\n    ReqwestError(reqwest::Error),\n    #[error(\"Unexpected status code: {0}\")]\n    UnexpectedStatusCode(u16),\n    #[error(\"Error parsing the request: {0}\")]\n    PluginRequestParseError(serde_json::error::Error),\n    #[error(\"Internal error from plugin {plugin_name}\")]\n    PluginInternalError {\n        plugin_name: String,\n        error: serde_json::Value,\n    },\n    #[error(\"User error from plugin {plugin_name}\")]\n    PluginUserError {\n        plugin_name: String,\n        error: serde_json::Value,\n    },\n}\n\n#[derive(Debug, thiserror::Error)]\npub enum BuildRequestError {\n    #[error(\"Invalid header name {header_name}: {error}\")]\n    InvalidHeaderName {\n        header_name: String,\n        #[source]\n        error: InvalidHeaderName,\n    },\n    #[error(\"Invalid header value for header {header_name}: {error}\")]\n    InvalidHeaderValue {\n        header_name: HeaderName,\n        #[source]","sourceCodeStart":13,"sourceCodeEnd":49,"githubUrl":"https://github.com/hasura/graphql-engine/blob/724551b9ae87845594ef0408cff0e50eb6c90dc5/v3/crates/plugins/pre-ndc-response-plugin/src/execute.rs#L13-L49","documentation":"The pre-NDC-response plugin flagged the response as failing a user-facing rule (a validation/policy check on the outgoing response). The plugin name and its JSON error detail are surfaced so the caller knows which rule rejected the response. This is an expected 4xx-class outcome, not a plugin bug.","triggerScenarios":"The hook inspects the outgoing NDC response and returns a user-error body, e.g. a data-redaction or row-limit policy finding forbidden fields, and the engine maps it to PluginUserError.","commonSituations":"Response-filtering plugins rejecting rows containing secret columns; row-count limit policies; plugins whose rules were tightened and now reject previously-allowed responses.","solutions":["Read the error detail to see which policy/rule triggered","Adjust the query (fields selected, filters) to comply with the plugin's rule","If the rule is too strict, update the plugin's policy configuration","Verify the rule change history — a config tightening often explains sudden failures"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"if let Err(Error::PluginUserError { plugin_name, error }) = res {\n    // return 4xx to client with the plugin's detail\n}","preventionTips":["Keep response policy rules documented and versioned","Test queries against policy rules in CI"],"tags":["rust","plugin","validation","policy","ndc"],"backgroundTag":"response-validation-rejected","analyzedSha":"724551b9ae87845594ef0408cff0e50eb6c90dc5","analyzedAt":"2026-08-28T07:32:55.105Z","schemaVersion":2},"datasetVersion":"2026-08-28T11:17:15.048Z"}