<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Adreet Gogoi</title>
    <description>The latest articles on DEV Community by Adreet Gogoi (@adreetgog).</description>
    <link>https://dev.to/adreetgog</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4119706%2F3df3689a-ed75-4790-aff1-f31c4fe9c73b.png</url>
      <title>DEV Community: Adreet Gogoi</title>
      <link>https://dev.to/adreetgog</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/adreetgog"/>
    <language>en</language>
    <item>
      <title>When a Tableau TSM HTTP 500 Wasn't Really an HTTP Problem</title>
      <dc:creator>Adreet Gogoi</dc:creator>
      <pubDate>Thu, 10 Sep 2026 17:33:20 +0000</pubDate>
      <link>https://dev.to/adreetgog/when-a-tableau-tsm-http-500-wasnt-really-an-http-problem-1kf9</link>
      <guid>https://dev.to/adreetgog/when-a-tableau-tsm-http-500-wasnt-really-an-http-problem-1kf9</guid>
      <description>&lt;p&gt;A real-world troubleshooting journey through Tableau Server, ZooKeeper, systemd, and a misleading No space left on device error.&lt;/p&gt;

&lt;p&gt;Sometimes infrastructure incidents are easy.&lt;/p&gt;

&lt;p&gt;A service is down.&lt;br&gt;
A port is closed.&lt;br&gt;
The logs tell you exactly what happened.&lt;/p&gt;

&lt;p&gt;And then there are incidents where everything looks almost healthy.&lt;/p&gt;

&lt;p&gt;That was the case with a Tableau Server environment I was troubleshooting.&lt;/p&gt;

&lt;p&gt;The symptom was straightforward:&lt;/p&gt;

&lt;p&gt;Tableau Services Manager (TSM) was returning HTTP 500 on port 8850.&lt;/p&gt;

&lt;p&gt;The interesting part was discovering that the HTTP 500 wasn't really the problem.&lt;/p&gt;

&lt;p&gt;The Architecture&lt;/p&gt;

&lt;p&gt;At a simplified level, the dependency chain looked like this:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;              Administrator
                   │
                   ▼
          Tableau TSM :8850
                   │
                   ▼
          TabadminController
                   │
                   ▼
         Tableau coordination
                   │
                   ▼
              ZooKeeper
                :8866
                   │
                   ▼
         Persistent storage
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;So rather than immediately restarting Tableau, I started at the symptom and worked down the dependency chain.&lt;/p&gt;

&lt;p&gt;Step 1: Is TSM Actually Down?&lt;/p&gt;

&lt;p&gt;The TSM CLI was reporting:&lt;/p&gt;

&lt;p&gt;Creating ServerApi object - host:ubuntu, port:8850&lt;/p&gt;

&lt;p&gt;Checking server health at &lt;a href="https://ubuntu:8850/health" rel="noopener noreferrer"&gt;https://ubuntu:8850/health&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Client request:&lt;br&gt;
GET &lt;a href="https://ubuntu:8850/api/0.5/status" rel="noopener noreferrer"&gt;https://ubuntu:8850/api/0.5/status&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;TabadminController did not return a TsmResponse.&lt;/p&gt;

&lt;p&gt;500 - Server Error&lt;/p&gt;

&lt;p&gt;The first question was simple:&lt;/p&gt;

&lt;p&gt;Is anything actually listening on 8850?&lt;/p&gt;

&lt;p&gt;sudo ss -lntp | grep 8850&lt;/p&gt;

&lt;p&gt;It showed Tableau's tabadmincontrol process listening on the port.&lt;/p&gt;

&lt;p&gt;So this wasn't a simple:&lt;/p&gt;

&lt;p&gt;Port closed → service down&lt;/p&gt;

&lt;p&gt;Something was alive.&lt;/p&gt;

&lt;p&gt;Step 2: Does the HTTPS Service Respond?&lt;/p&gt;

&lt;p&gt;I tested the endpoint locally:&lt;/p&gt;

&lt;p&gt;curl -k -s &lt;a href="https://localhost:8850/health" rel="noopener noreferrer"&gt;https://localhost:8850/health&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It returned:&lt;/p&gt;

&lt;p&gt;{&lt;br&gt;
  "status": 403,&lt;br&gt;
  "error": "Forbidden"&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;A 403 might initially look like another failure.&lt;/p&gt;

&lt;p&gt;But it actually told us something important:&lt;/p&gt;

&lt;p&gt;The TabadminController was reachable and responding.&lt;/p&gt;

&lt;p&gt;So the investigation moved further down the stack.&lt;/p&gt;

&lt;p&gt;This is one of the most useful troubleshooting habits:&lt;/p&gt;

&lt;p&gt;Don't just look at the error code. Ask what the response proves.&lt;/p&gt;

&lt;p&gt;In this case, 403 proved that the HTTPS service was alive.&lt;/p&gt;

&lt;p&gt;Step 3: Follow the Dependency Chain&lt;/p&gt;

&lt;p&gt;Tableau Server depends on several internal services, including its coordination layer.&lt;/p&gt;

&lt;p&gt;The Tableau-managed ZooKeeper instance was:&lt;/p&gt;

&lt;p&gt;appzookeeper_0&lt;/p&gt;

&lt;p&gt;I checked its Tableau service status:&lt;/p&gt;

&lt;p&gt;sudo /var/opt/tableau/tableau_server/data/tabsvc/services/appzookeeper_0.20253.26.0206.0336/status.sh&lt;/p&gt;

&lt;p&gt;The important part of the output was:&lt;/p&gt;

&lt;p&gt;"details" : {&lt;br&gt;
  "message" : "Connection refused"&lt;br&gt;
},&lt;br&gt;
"processStatus" : "DOWN"&lt;/p&gt;

&lt;p&gt;That was our first strong indication that the problem was deeper than TSM.&lt;/p&gt;

&lt;p&gt;Next:&lt;/p&gt;

&lt;p&gt;sudo ss -lntp | grep 8866&lt;/p&gt;

&lt;p&gt;Nothing was listening.&lt;/p&gt;

&lt;p&gt;So we now had:&lt;/p&gt;

&lt;p&gt;TSM :8850&lt;br&gt;
     │&lt;br&gt;
     ▼&lt;br&gt;
TabadminController&lt;br&gt;
     │&lt;br&gt;
     ▼&lt;br&gt;
ZooKeeper&lt;br&gt;
     │&lt;br&gt;
     ✕&lt;br&gt;
   :8866&lt;/p&gt;

&lt;p&gt;The HTTP 500 was beginning to look like a downstream dependency failure.&lt;/p&gt;

&lt;p&gt;Step 4: The Logs Finally Told the Story&lt;/p&gt;

&lt;p&gt;This was the turning point.&lt;/p&gt;

&lt;p&gt;Instead of continuing to restart services, I went to the ZooKeeper logs.&lt;/p&gt;

&lt;p&gt;There it was:&lt;/p&gt;

&lt;p&gt;Severe unrecoverable error, from thread : SyncThread:0&lt;/p&gt;

&lt;p&gt;java.io.IOException: No space left on device&lt;/p&gt;

&lt;p&gt;The stack trace pointed into ZooKeeper's transaction-log handling:&lt;/p&gt;

&lt;p&gt;org.apache.zookeeper.server.persistence.FileTxnLog.append(...)&lt;/p&gt;

&lt;p&gt;Immediately afterwards:&lt;/p&gt;

&lt;p&gt;SyncThread:0 ... Thread exits, error code 1&lt;/p&gt;

&lt;p&gt;Then:&lt;/p&gt;

&lt;p&gt;SingleNodeZookeeperWrapper - Attempting to shutdown zookeeper instance.&lt;/p&gt;

&lt;p&gt;And finally:&lt;/p&gt;

&lt;p&gt;NettyServerCnxnFactory - shutdown called ... :8866&lt;/p&gt;

&lt;p&gt;Now the failure chain was clear:&lt;/p&gt;

&lt;p&gt;ZooKeeper tries to write transaction log&lt;br&gt;
                │&lt;br&gt;
                ▼&lt;br&gt;
       Filesystem returns ENOSPC&lt;br&gt;
                │&lt;br&gt;
                ▼&lt;br&gt;
       ZooKeeper critical thread exits&lt;br&gt;
                │&lt;br&gt;
                ▼&lt;br&gt;
          ZooKeeper shuts down&lt;br&gt;
                │&lt;br&gt;
                ▼&lt;br&gt;
          Port 8866 disappears&lt;br&gt;
                │&lt;br&gt;
                ▼&lt;br&gt;
 Tableau coordination becomes unavailable&lt;br&gt;
                │&lt;br&gt;
                ▼&lt;br&gt;
       TSM API request fails&lt;br&gt;
                │&lt;br&gt;
                ▼&lt;br&gt;
             HTTP 500&lt;/p&gt;

&lt;p&gt;That was the real story.&lt;/p&gt;

&lt;p&gt;The Strange Part: The Disk Wasn't Full&lt;/p&gt;

&lt;p&gt;Naturally, the next command was:&lt;/p&gt;

&lt;p&gt;df -h&lt;/p&gt;

&lt;p&gt;And the result was surprising:&lt;/p&gt;

&lt;p&gt;/dev/vda1    991G    181G    811G    19%    /&lt;/p&gt;

&lt;p&gt;There was 811 GB free.&lt;/p&gt;

&lt;p&gt;So what exactly did No space left on device mean?&lt;/p&gt;

&lt;p&gt;This is where the investigation needs to be precise.&lt;/p&gt;

&lt;p&gt;ENOSPC does not necessarily mean the entire filesystem has reached 100% capacity.&lt;/p&gt;

&lt;p&gt;Possible explanations include:&lt;/p&gt;

&lt;p&gt;inode exhaustion&lt;br&gt;
filesystem quotas&lt;br&gt;
a full temporary filesystem&lt;br&gt;
application-specific storage limits&lt;br&gt;
deleted-but-open files&lt;br&gt;
filesystem or storage errors&lt;/p&gt;

&lt;p&gt;So I checked additional evidence rather than simply declaring:&lt;/p&gt;

&lt;p&gt;"The disk was full."&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;p&gt;df -i&lt;/p&gt;

&lt;p&gt;checks inode usage.&lt;/p&gt;

&lt;p&gt;And:&lt;/p&gt;

&lt;p&gt;sudo lsof +L1&lt;/p&gt;

&lt;p&gt;can reveal deleted files that processes still have open.&lt;/p&gt;

&lt;p&gt;In this environment, Tableau processes were holding deleted files open, including a large deleted Hyper temporary file.&lt;/p&gt;

&lt;p&gt;However, that alone was not enough to prove that those files directly caused ZooKeeper's ENOSPC event.&lt;/p&gt;

&lt;p&gt;That distinction matters.&lt;/p&gt;

&lt;p&gt;The evidence proves:&lt;/p&gt;

&lt;p&gt;ZooKeeper's transaction-log write received ENOSPC.&lt;/p&gt;

&lt;p&gt;It does not prove exactly which underlying storage condition produced that response.&lt;/p&gt;

&lt;p&gt;Good troubleshooting means knowing the difference between evidence and hypothesis.&lt;/p&gt;

&lt;p&gt;Another Trap: systemd Said ZooKeeper Was Running&lt;/p&gt;

&lt;p&gt;There was another confusing clue.&lt;/p&gt;

&lt;p&gt;Checking the Tableau user-level systemd service:&lt;/p&gt;

&lt;p&gt;sudo -u tableau \&lt;br&gt;
XDG_RUNTIME_DIR=/run/user/999 \&lt;br&gt;
systemctl --user status appzookeeper_0&lt;/p&gt;

&lt;p&gt;reported:&lt;/p&gt;

&lt;p&gt;Active: active (running)&lt;br&gt;
Main PID: 881&lt;/p&gt;

&lt;p&gt;But:&lt;/p&gt;

&lt;p&gt;sudo ss -lntp | grep 8866&lt;/p&gt;

&lt;p&gt;showed nothing.&lt;/p&gt;

&lt;p&gt;And Tableau's own status script said:&lt;/p&gt;

&lt;p&gt;processStatus : DOWN&lt;/p&gt;

&lt;p&gt;So which one was correct?&lt;/p&gt;

&lt;p&gt;The answer is: they were measuring different things.&lt;/p&gt;

&lt;p&gt;systemd knew that the process existed.&lt;/p&gt;

&lt;p&gt;Tableau knew that the application wasn't functioning.&lt;/p&gt;

&lt;p&gt;The port check knew that nothing was accepting connections.&lt;/p&gt;

&lt;p&gt;The logs explained why.&lt;/p&gt;

&lt;p&gt;This is why a single:&lt;/p&gt;

&lt;p&gt;systemctl status&lt;/p&gt;

&lt;p&gt;should never be treated as the complete definition of application health.&lt;/p&gt;

&lt;p&gt;I like to think of it as four independent signals:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;      Process
         +
       Port
         +
  Application health
         +
        Logs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;When those disagree, investigate the disagreement.&lt;/p&gt;

&lt;p&gt;One More Lesson: start Is Not Always restart&lt;/p&gt;

&lt;p&gt;Because systemd believed the service was already running, simply doing:&lt;/p&gt;

&lt;p&gt;systemctl --user start appzookeeper_0&lt;/p&gt;

&lt;p&gt;didn't necessarily give us a fresh application process.&lt;/p&gt;

&lt;p&gt;The service manager already believed:&lt;/p&gt;

&lt;p&gt;Active: active (running)&lt;/p&gt;

&lt;p&gt;So the appropriate operational action was a restart, executed in the Tableau user's systemd context:&lt;/p&gt;

&lt;p&gt;sudo -u tableau \&lt;br&gt;
XDG_RUNTIME_DIR=/run/user/999 \&lt;br&gt;
systemctl --user restart appzookeeper_0&lt;/p&gt;

&lt;p&gt;The important part here isn't the command itself.&lt;/p&gt;

&lt;p&gt;It's understanding who owns the service.&lt;/p&gt;

&lt;p&gt;Using:&lt;/p&gt;

&lt;p&gt;sudo systemctl ...&lt;/p&gt;

&lt;p&gt;and:&lt;/p&gt;

&lt;p&gt;sudo -u tableau systemctl --user ...&lt;/p&gt;

&lt;p&gt;are not equivalent.&lt;/p&gt;

&lt;p&gt;A user-level systemd service belongs to its user session, so operating it in the wrong context can produce errors such as:&lt;/p&gt;

&lt;p&gt;Failed to connect to bus&lt;br&gt;
What I Took Away From the Incident&lt;/p&gt;

&lt;p&gt;The actual fix was only one part of the exercise.&lt;/p&gt;

&lt;p&gt;The bigger lesson was the troubleshooting methodology.&lt;/p&gt;

&lt;p&gt;When TSM reported:&lt;/p&gt;

&lt;p&gt;HTTP 500&lt;/p&gt;

&lt;p&gt;I could have immediately restarted Tableau.&lt;/p&gt;

&lt;p&gt;Instead, I asked:&lt;/p&gt;

&lt;p&gt;Is 8850 listening?&lt;br&gt;
        ↓&lt;br&gt;
Yes.&lt;/p&gt;

&lt;p&gt;Does the HTTPS service respond?&lt;br&gt;
        ↓&lt;br&gt;
Yes.&lt;/p&gt;

&lt;p&gt;Is ZooKeeper healthy?&lt;br&gt;
        ↓&lt;br&gt;
No.&lt;/p&gt;

&lt;p&gt;Is 8866 listening?&lt;br&gt;
        ↓&lt;br&gt;
No.&lt;/p&gt;

&lt;p&gt;Why did ZooKeeper stop?&lt;br&gt;
        ↓&lt;br&gt;
Read the logs.&lt;/p&gt;

&lt;p&gt;What happened?&lt;br&gt;
        ↓&lt;br&gt;
ENOSPC while writing the transaction log.&lt;/p&gt;

&lt;p&gt;Every step reduced the search space.&lt;/p&gt;

&lt;p&gt;What I Would Monitor in Production&lt;/p&gt;

&lt;p&gt;After an incident like this, monitoring should go beyond CPU, memory and overall disk usage.&lt;/p&gt;

&lt;p&gt;I'd monitor:&lt;/p&gt;

&lt;p&gt;Filesystem capacity&lt;br&gt;
df -h&lt;br&gt;
Inodes&lt;br&gt;
df -i&lt;br&gt;
Deleted-but-open files&lt;br&gt;
sudo lsof +L1&lt;br&gt;
Critical Tableau ports&lt;br&gt;
8850 → TSM&lt;br&gt;
8866 → ZooKeeper&lt;br&gt;
Application health&lt;/p&gt;

&lt;p&gt;A process being alive isn't enough.&lt;/p&gt;

&lt;p&gt;A port being open isn't enough.&lt;/p&gt;

&lt;p&gt;A service showing active (running) isn't enough.&lt;/p&gt;

&lt;p&gt;The application must actually be functional.&lt;/p&gt;

&lt;p&gt;The Bigger Lesson: Troubleshooting Is an Art&lt;/p&gt;

&lt;p&gt;This incident started with:&lt;/p&gt;

&lt;p&gt;"Tableau TSM is returning HTTP 500."&lt;/p&gt;

&lt;p&gt;But the real failure was several layers below it.&lt;/p&gt;

&lt;p&gt;That's what makes infrastructure troubleshooting interesting.&lt;/p&gt;

&lt;p&gt;The goal isn't to memorize every possible command.&lt;/p&gt;

&lt;p&gt;It's to know what question to ask next.&lt;/p&gt;

&lt;p&gt;Don't assume an HTTP error is an HTTP problem.&lt;/p&gt;

&lt;p&gt;Don't assume a running process is a healthy application.&lt;/p&gt;

&lt;p&gt;Don't assume df -h showing free space means ENOSPC is impossible.&lt;/p&gt;

&lt;p&gt;And don't confuse a possible explanation with a proven root cause.&lt;/p&gt;

&lt;p&gt;Instead:&lt;/p&gt;

&lt;p&gt;Follow the evidence.&lt;/p&gt;

&lt;p&gt;Check the port.&lt;/p&gt;

&lt;p&gt;Check the process.&lt;/p&gt;

&lt;p&gt;Check the dependency.&lt;/p&gt;

&lt;p&gt;Read the logs.&lt;/p&gt;

&lt;p&gt;Challenge your assumptions.&lt;/p&gt;

&lt;p&gt;Then fix the layer that's actually broken.&lt;/p&gt;

&lt;p&gt;Because sometimes the most misleading part of an incident is the very first error you see.&lt;/p&gt;

&lt;p&gt;In this case:&lt;/p&gt;

&lt;p&gt;HTTP 500&lt;/p&gt;

&lt;p&gt;was merely the messenger.&lt;/p&gt;

&lt;p&gt;The real story was:&lt;/p&gt;

&lt;p&gt;ZooKeeper&lt;br&gt;
   ↓&lt;br&gt;
Transaction log write&lt;br&gt;
   ↓&lt;br&gt;
ENOSPC&lt;br&gt;
   ↓&lt;br&gt;
ZooKeeper shutdown&lt;br&gt;
   ↓&lt;br&gt;
Coordination failure&lt;br&gt;
   ↓&lt;br&gt;
TSM HTTP 500&lt;/p&gt;

&lt;p&gt;And that, to me, is the essence of everyday systems troubleshooting:&lt;/p&gt;

&lt;p&gt;The art isn't knowing every answer.&lt;br&gt;
The art is knowing where to look next.&lt;/p&gt;

</description>
      <category>tableau</category>
      <category>ai</category>
      <category>sysad</category>
      <category>sre</category>
    </item>
  </channel>
</rss>
