What Apps Script actually requires from a web app function
When you deploy a script as a web app, Apps Script routes incoming HTTP requests to doGet (for GET) or doPost (for POST). Those functions must explicitly return one of two object types: an HtmlOutput, built via HtmlService, or a TextOutput, built via ContentService. Returning nothing, returning undefined, returning a plain string, or returning a raw JavaScript object all produce the same error: "The script completed but did not return anything."
The most common cause is a conditional branch that exits without a return statement. You add an if block for the happy path, wire it up, and forget that any request hitting the else case falls off the end of the function with an implicit undefined return. Apps Script cannot render undefined as HTTP output, so it surfaces that message to the browser.
The fix is mechanical: every reachable exit point in doGet and doPost must return HtmlService.createHtmlOutput(), HtmlService.createHtmlOutputFromFile(), or ContentService.createTextOutput(). A catch-all return at the bottom of the function, after all your conditionals, handles everything else. The snippet above shows that pattern: two conditional branches, each returning the correct type, and a fallback return as the last line.