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

  1. Re-authorize the Google Sheets datasource if the OAuth token expired.
  2. Enable plugin HTTP logging to capture the real status and body.
  3. Confirm the sheet name and range in the request are valid and exist.
  4. 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

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.