appsmithorg/appsmith · error · AppsmithPluginException
PE-GSH-5000
PE-GSH-5000
Error message
Missing a valid response object.
What it means
Thrown by GetStructureMethod.transformExecutionResponse (GetStructureMethod.java:110) with code PE-GSH-5000. After the Sheets API `values:batchGet` returns, the transform reads `response.get("valueRanges")` to extract headers and sample rows. If the response node itself is null, it throws before that read.
Source
Thrown at app/server/appsmith-plugins/googleSheetsPlugin/src/main/java/com/external/config/GetStructureMethod.java:110
if (tableHeaderIndex <= 0) {
tableHeaderIndex = 1;
}
} catch (NumberFormatException e) {
// Should have already been caught
}
}
return List.of(
"'" + methodConfig.getSheetName() + "'!" + tableHeaderIndex + ":" + tableHeaderIndex,
"'" + methodConfig.getSheetName() + "'!" + (tableHeaderIndex + rowOffset + 1) + ":"
+ (tableHeaderIndex + rowOffset + rowLimit));
}
@Override
public JsonNode transformExecutionResponse(
JsonNode response, MethodConfig methodConfig, Set<String> userAuthorizedSheetIds) {
if (response == null) {
throw new AppsmithPluginException(
GSheetsPluginError.QUERY_EXECUTION_FAILED, ErrorMessages.MISSING_VALID_RESPONSE_ERROR_MSG);
}
ArrayNode valueRanges = (ArrayNode) response.get("valueRanges");
ArrayNode headers = (ArrayNode) valueRanges.get(0).get("values");
ArrayNode values =
valueRanges.get(1) != null ? (ArrayNode) valueRanges.get(1).get("values") : null;
int valueSize = 0;
if (headers == null || headers.isEmpty()) {
return this.objectMapper.createArrayNode();
}
if (values != null) {
for (int i = 0; i < values.size(); i++) {
valueSize = Math.max(valueSize, values.get(i).size());
}
}
View on GitHub (pinned to 8cd9021c24)
Solutions
- Re-authorize the Google Sheets datasource if the OAuth token expired.
- Enable plugin HTTP logging to capture the real status and body.
- Confirm the sheet name and range in the request are valid and exist.
- If patching the plugin, coerce a null body to an empty object node before invoking transformExecutionResponse.
Example fix
// before — batchGet body arrives null
public JsonNode transformExecutionResponse(JsonNode response, ...) {
if (response == null) { throw ...; }
ArrayNode valueRanges = (ArrayNode) response.get("valueRanges");
...
}
// after — caller coerces empty
JsonNode node = body == null ? objectMapper.createObjectNode() : objectMapper.readTree(body);
return method.transformExecutionResponse(node, config, ids); Defensive patterns
Strategy: retry
Try / catch
async function getStructureSafe() {
try { return await getStructure.run(); }
catch (e) {
if (e && /Missing a valid response object/.test(e.message)) {
await refreshToken();
return await getStructure.run();
}
throw e;
}
} Prevention
- Keep OAuth credentials valid to avoid non-JSON 401 bodies being nulled.
- Verify the sheet name and range exist before reading structure.
- Enable HTTP logging to inspect the batchGet response.
When it happens
Trigger: The batchGet request returned a body that arrived null at the transform — empty body, a nulled non-JSON error, or a Webclient filter consuming the body. The null guard is the first statement of transformExecutionResponse.
Common situations: OAuth token expired (401 body nulled upstream); the requested range/sheet does not exist so the API returned an unexpected empty body; transient network failure returning empty; rate limit (429) error body lost in error mapping.
Related errors
AI-assisted analysis of appsmithorg/appsmith@8cd9021c24 (2026-08-12).
Data as JSON: /api/errors/40d9012095cc364e.
Report an issue: GitHub.