cube-js/cube · error · Error

Unterminated string: ${sql}

Error message

Unterminated string: ${sql}

What it means

Thrown by SqlParser.sqlUpperCase(), a pre-parse pass that uppercases SQL outside string literals while tracking quote state ('', "", ``) and comments. If the scan ends with a quote character still open, the string was never closed and the parser throws a plain Error 'Unterminated string: <sql>' with the whole SQL text.

Source

Thrown at packages/cubejs-schema-compiler/src/parser/SqlParser.ts:106

        // Check for start of single-line comment
        commentType = '--';
        result += sql[i];
      } else if (sql[i] === '/' && sql[i + 1] === '*') {
        // Check for start of multi-line comment
        commentType = '/*';
        result += sql[i];
      } else if (sql[i] === '\'' || sql[i] === '"' || sql[i] === '`') {
        // Check for string literals
        openChar = sql[i];
        result += sql[i];
      } else {
        // Regular character - convert to uppercase
        result += sql[i].toUpperCase();
      }
    }

    if (openChar) {
      throw new Error(`Unterminated string: ${sql}`);
    }

    return result;
  }

  protected parse() {
    const { sql } = this;

    const chars = new CharStream(SqlParser.sqlUpperCase(sql));
    chars.getText = (start, stop) => {
      if (stop >= chars.size) {
        stop = chars.size - 1;
      }

      if (start >= chars.size) {
        return '';
      } else {
        return sql.slice(start, stop + 1);

View on GitHub (pinned to 7d981676b3)

Solutions

  1. Inspect the SQL in the error message and add the missing closing quote
  2. Validate the SQL in your database client first; only pass executable statements to the parser
  3. Remove quotes from comments or move them before the comment to avoid tracker confusion
  4. Escape embedded quotes properly (double them: 'it''s') instead of unescaped apostrophes

Example fix

// before
const p = new SqlParser("SELECT * FROM t WHERE name = 'foo");
// after
const p = new SqlParser("SELECT * FROM t WHERE name = 'foo'");
Defensive patterns

Strategy: try-catch

Validate before calling

function hasBalancedQuotes(sql) {
  let open = null;
  for (let i = 0; i < sql.length; i++) {
    if (open) { if (sql[i] === open) open = null; }
    else if (sql[i] === "'" || sql[i] === '"' || sql[i] === '`') open = sql[i];
  }
  return open === null;
}
if (!hasBalancedQuotes(sql)) throw new Error('SQL has an unterminated string literal');

Try / catch

try {
  new SqlParser(sql); // or extractTableFrom(sql)
} catch (e) {
  if (/Unterminated string/.test(e.message)) {
    throw new Error('Check quotes in: ' + sql.slice(0, 120));
  }
  throw e;
}

Prevention

When it happens

Trigger: Constructing a SqlParser (parse() -> sqlUpperCase) with SQL containing a quote ('...", "... or `...) that is never closed, including quotes inside comments the tracker mishandles or escaped-quote edge cases.

Common situations: Dynamically built SQL passed to scaffolding/query introspection APIs with a missing closing quote, copy-pasted SQL truncated mid-literal, or SQL where a quote appears in a -- comment causing mis-tracking.

Related errors


AI-assisted analysis of cube-js/cube@7d981676b3 (2026-09-02). Data as JSON: /api/errors/e391cc4211ff7354. Report an issue: GitHub.