Can't post a reply to a topic whose title matches numberWord

We use the API to create topics in discourse programatically. When the title of the topic to create is already in use, Discourse typically returns a 403, which we use to indicate we should get the id of the topic with that name and use the API to submit a reply to that topic instead. This works for most cases.

When the topic title contains a word of the form \(1-9)+(a-zA-z)+\ (some number of numbers, followed immediately by some number of letters with no space or other characters in between), the attempt to create a topic returns a 404 instead of a 403. Attempting to get a topic ID at this point returns undefined as the topic id, and so posting a reply doesn’t work.

I think this has to do with how we’re guessing the topic slug - we’re using lodash’s kebabCase method on the entire title, which turns 1hello to 1-hello, whereas in the real slug, the word is left as 1hello.

What method is Discourse using to turn titles into slugs? One issue is that kebab-casing a title to turn into a slug, then grabbing the id from the redirect URL has been discussed in the forum before as the only way to get a topic ID from the API.

A snippet of our code where the bug occurs:

const getIdFromTopicRedirect = path => {
  const splits = _.split(path, "/");
  return splits[splits.length - 1];

const createTopicOrReply = params => {
  const queryString = "?api_key=" + process.env.api_key + "&api_username=" + process.env.api_username + "&category=" + process.env.discourse_category + "&raw=" + encodeURIComponent(params.raw) +
    "&title=" + params.title;
    .post(process.env.discourse_base_url + "/posts.json" + queryString)
    .then(discourseResponse => {
      console.log({ discourseResponse });
    .catch(error => {
      const slug = _.kebabCase(params.title);
      // get topic id from title by turning it into a url slug, and getting the id from the redirect.
      // gross but apparently the only supported way to get a topic
      axios.get(process.env.discourse_base_url + "/t/" + slug)
        .then((req, res) => {
          console.log({ req: req, res: res });
          // not logged in, so this should never happen
        .catch((req, res) => {
          console.log({ req: req, res: res });
          // this topic exists but we're not authorized to see it since we're not logged in
          // this is the expected behavior
          if (req.response.status === 403) {
            const topicId = getIdFromTopicRedirect(req.request.path);
            console.log({topicId: topicId});

            const replyQueryString = "?api_key=" + process.env.api_key + "&api_username=" + process.env.api_username + "&raw=" + encodeURIComponent(params.raw) + "&topic_id=" + topicId;
   + "/posts.json" + replyQueryString)
              .then((req, res) => {
1 Like