4 Replies Latest reply on Mar 24, 2014 7:09 AM by Brad Lynch

    •Could not locate unexpired trusted ticket

    Andrei Rukavina

      Hi Experts!


      I'm creating a custom app for printing views as parts of an only PDF file.


      The app's main goal is to print views of this form:



      The app uses the JS API to get info about selected filters and performs several custom PNG prints. To get the PNG representing the view, I use above URI from server side, due to Cross Origin Browser restrictions. The server side is a JAVA web app on Tomcat 7.0.42. In that java app is where I ask for the ticket to tableau and it successfully response my request. After this, while holding the ticket, I try to get the PNG content, but this error message shows up:

      An error occurred on the server.  The details of the error are:
      • Could not locate unexpired trusted ticket 156237713
      Click the Refresh button in your web browser and try again.
      If you continue to receive this error please contact your Tableau Server Administrator.

      My JAVA code is as follows (remember is being developed right now)


      package com.comp.tableau.cli.server;
      import java.io.BufferedReader;
      import java.io.IOException;
      import java.io.InputStreamReader;
      import java.io.OutputStreamWriter;
      import java.net.URL;
      import java.net.URLConnection;
      import java.net.URLEncoder;
      import javax.servlet.ServletException;
      import javax.servlet.http.HttpServletRequest;
      import javax.servlet.http.HttpServletResponse;
      * Servlet implementation class for Servlet: TableauServlet
      public class TableauServlet extends javax.servlet.http.HttpServlet {
          private static final long serialVersionUID = 1L;
          public TableauServlet() {
          protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
              final String user = "arukavina";
              final String wgserver = "myserver";
              final String dst = "t/<Site>/views/<Workbook>/<WorkSheet>";
              final String params = "";
              String ticket = getTrustedTicket(wgserver, user, request.getRemoteAddr());
              if ( !ticket.equals("-1") ) {
                  System.out.println("Ticket is: " + ticket);
                  System.out.println("Accessing: " + "http://" + wgserver + "/trusted/" + ticket + "/" + dst);
                  response.setHeader("Location", "http://" + wgserver + "/trusted/" + ticket + "/" + dst + "?" + params);
              else {
                  // handle error
                  System.out.println("Invalid ticket id: " + ticket);
                  throw new ServletException("Invalid ticket " + ticket);
          // the client_ip parameter isn't necessary to send in the POST unless you have
          // wgserver.extended_trusted_ip_checking enabled (it's disabled by default)
          private String getTrustedTicket(String wgserver, String user, String remoteAddr)
              throws ServletException
              OutputStreamWriter out = null;
              BufferedReader in = null;
              try {
                  // Encode the parameters
                  StringBuffer data = new StringBuffer();
                  data.append(URLEncoder.encode("username", "UTF-8"));
                  data.append(URLEncoder.encode(user, "UTF-8"));
                  data.append(URLEncoder.encode("client_ip", "UTF-8"));
                  data.append(URLEncoder.encode(remoteAddr, "UTF-8"));
                  // Send the request
                  URL url = new URL("http://" + wgserver + "/trusted");
                  URLConnection conn = url.openConnection();
                  out = new OutputStreamWriter(conn.getOutputStream());
                  // Read the response
                  StringBuffer rsp = new StringBuffer();
                  in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
                  String line;
                  while ( (line = in.readLine()) != null) {
                  System.out.println("Rsp is: " + rsp.toString());
                  return rsp.toString();
              } catch (Exception e) {
                  throw new ServletException(e);
              finally {
                  try {
                      if (in != null) in.close();
                      if (out != null) out.close();
                  catch (IOException e) {}


      I'm also attaching a PHP version of the same code, getting the exact same error.


      This issue is blocking our development so it's quite urgent.

      I'm looking forward to listening from you.


      Thanks in advance.


      Andrei Rukavina.

        • 1. Re: •Could not locate unexpired trusted ticket
          Andrei Rukavina

          Ok, this was the problem:


          My Tableau Server has Multi-Site feature enabled so, in order to use the ticket number, you have to access the right site. But, what isn't explained is that you also have to ask that ticket for that specific site.


          Here is an example:


          $username = 'userX';
          $site_id = 'siteX';
          $data = array('username' => $username, 'target_site' => $site_id);
          $result = post_request('http://'.$server.'/trusted', $data);
          $ticket = $result['content'];

          NOTE: post_request function is showed at the end.


          The parameter target_site have to be sent in the post request in order to have the result ticket working for later content requisition.

          To use this target_site specific ticket:



          $fullUrl = "http://{$server}/trusted/{$ticket}/t/{$site}/{$path}?{$query}";
          if ($ticket <= 0) {
           throw new Exception("Server did not return a valid ticket.");
          $ch = curl_init();
          curl_setopt_array($ch, array(
              CURLOPT_URL => $fullUrl,
              CURLOPT_FOLLOWLOCATION => true,
              CURLOPT_COOKIEFILE => '',
              CURLOPT_POST => false,
              CURLOPT_FAILONERROR => true,
              CURLOPT_RETURNTRANSFER => true,
              CURLOPT_TIMEOUT => 400


          That was the trick.


          Hope you find this useful.





          post_request function:


          function post_request($url, $data, $referer='') {
              // Convert the data array into URL Parameters like a=b&foo=bar etc.
              $data = http_build_query($data);
              // parse the given URL
              $url = parse_url($url);
              if ($url['scheme'] != 'http') {
                  die('Error: Only HTTP request are supported !');
              // extract host and path:
              $host = $url['host'];
              $path = $url['path'];
              // open a socket connection on port 80 - timeout: 30 sec
              $fp = fsockopen($host, 80, $errno, $errstr, 30);
              if ($fp){
                  // send the request headers:
                  fputs($fp, "POST $path HTTP/1.1\r\n");
                  fputs($fp, "Host: $host\r\n");
                  if ($referer != '')
                      fputs($fp, "Referer: $referer\r\n");
                  fputs($fp, "Content-type: application/x-www-form-urlencoded\r\n");
                  fputs($fp, "Content-length: ". strlen($data) ."\r\n");
                  fputs($fp, "Connection: close\r\n\r\n");
                  fputs($fp, $data);
                  $result = '';
                  while(!feof($fp)) {
                      // receive the results of the request
                      $result .= fgets($fp, 128);
              else {
                  return array(
                      'status' => 'err',
                      'error' => "$errstr ($errno)"
              // close the socket connection:
              // split the result header from the content
              $result = explode("\r\n\r\n", $result, 2);
              $header = isset($result[0]) ? $result[0] : '';
              $content = isset($result[1]) ? ''.$result[1] : '';
              // return as structured array:
              return array(
                  'status' => 'ok',
                  'header' => $header,
                  'content' => $content
          • 2. Re: •Could not locate unexpired trusted ticket
            Jim Wahl

            Thanks for posting the solution. This is helpful.


            You probably know this, but you can have Tableau return a PDF by using the .pdf extension on the request. This gives you a much higher quality PDF report. The one irritating aspect is that Tableau defaults to a Letter page size which results in a lot of white space, and requires you to some some additional PDF processing.



            • 3. Re: •Could not locate unexpired trusted ticket
              Andrei Rukavina

              Hi Jim!


              Yes, that's true. But I had to print several dashboards into a single PDF file. I didn't find any JS Open Source Lib, to merge PDF files. This is why I've chosen creating the PDF my self, pasting PNG images into it.




              • 4. Re: •Could not locate unexpired trusted ticket
                Brad Lynch

                Wanted to thank you for posting the solution.


                While we aren't using PHP, we were banging our heads against this error with a multi-site configuration.


                Once we implemented your solution:


                "My Tableau Server has Multi-Site feature enabled so, in order to use the ticket number, you have to access the right site. But, what isn't explained is that you also have to ask that ticket for that specific site."


                We were good to go. We didn't see this in any documentation so really appreciated this thread.