It's rather interesting, I tried to replicate the error in a console application and it worked just fine, no null reference, even though the logic is the same. Initially I thought it might have to do with scope. The code that's giving me the error is an ASP.NET webpage and the Thread logic is exactly as described in the first post.

In global scope, I have defined  a thread and a static list (which is modified by such thread) and set thread to null and list to new List<string>().

I did a workaround by checking that the list has been changed, if not the it goes back to the old way of fetching after user request. Still, it intrigues me why that error is being thrown and I seem unable to replicate, it'll probably be something very simple I'm overlooking.

All the relevant code:

protected void ddlRelTypeSelected(object sender, EventArgs e)
        {
            lblRelType.Visible = true;

            string strALPHA ="";
           
            if (ddlPlatform.Text == "IVWEX")
            {
                if (ddlType.Text == "QA.Builds" || ddlType.Text == "QA.Releases")
                {
                    // ...
                }
                else
                {
                    // ...
                }
            }
            else if (ddlPlatform.Text == "ALPHA")
            {
                if (ddlType.Text == "QA.Candidates")
                {
                    // ...
                }
                else if (ddlType.Text == "SCM.QA_Images")
                {
                    // ...
                }
                else    // QA.Releases
                {
                    strALPHA = ConfigurationManager.AppSettings["RelTypeAlpha"].ToString();
                    string[] arrALPHA = strALPHA.Split(chsep);
                    ddlRelType.DataSource = arrALPHA;

                    // spawn osThread and gameThread
                    osThread = new Thread(() => getReleases("OS", alist));   
                    osThread.Start();
                    gamesThread = new Thread(() => getReleases("GAMES", blist));
                    gamesThread.Start();
                }
                ddlRelType.DataBind();
                ddlRelType.Visible = true;
            }     
        }

        private void getReleases(string SType, List<string> list)
        {
            OleDbConnection conn = openDBConnection(SCMSoftwareReleases);
            string SqlQuery = " SELECT Release_Label as ReleaseLabel, Release_Type as ReleaseType "
                      + " FROM TrackReleases "
                      + " WHERE Platform = 'ALPHA' "
                      + " AND Software_Type = '" + SType + "' "
                      + " AND Release_Type <> 'Branch' "
                      + " ORDER by 1 asc";
            OleDbDataAdapter da = new OleDbDataAdapter(SqlQuery, conn);
            DataSet ds = new DataSet();
            da.Fill(ds);
            DataRowCollection drc = ds.Tables["Table"].Rows;    // Table is default name
            list.Clear();
            
            for (int i = 0; i < drc.Count; ++i)
                list.Add(drc[i][0].ToString() + "|" + drc[i][1].ToString());
            if (conn != null)
                conn.Close();
        }

        private void fetchReleases(ref string[] arr, string SType)
        {
            osThread.Join();    // null reference exception
            
            // ...
        }

The webpage is a bunch of dropdown menus that leads to the sql query. The threads are without a doubt being spawned before fetchReleases is called because fetchReleases can only be called from a UI element that only appears after the call to ddlRelTypeSelected event handler.

Actually stepping through the debugger shows me that threads are finished before going to fetchReleases.
Now I'm thinking it's due to being a web app, the stateless nature of web apps has gotten me again?!