Hacktoberfest 2026: the issues maintainers tagged for October, open and beginner-friendly. Browse Hacktoberfest issues

bug: `{{base_url}}` doubles the base_url — the README's own example produces a malformed URL

Open Beginner friendly
#45 0 comments 0 reactions 0 assignees View on GitHub

Maintainers usually reply within 1 day

Nobody has claimed this yet.

Assessment

Difficulty
2/5
Estimated time
1-3 hours
Newbie friendliness
78/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Active
Tech stack
python
Domain
cli

Research direction

Start with build_request_parts in git_api/cli.py and compare its URL handling with replay around line 427. Add a regression test alongside the URL/interpolation coverage in tests/test_cli.py for the README example, then run the relevant tests. Done means the example resolves to https://api.example.com/users without doubling the base URL.

Written by the indexing model from the issue text.

Description

bug

Summary

resolve_url() runs before _interpolate(), so the base_url prefix is glued on before the {{base_url}} placeholder is expanded. The result is a doubled URL — and this is exactly the example the README presents as the headline demo of the tool.

Steps to reproduce

mkdir /tmp/gapi-demo && cd /tmp/gapi-demo
git init -q
git-api

Inside the REPL — this is the README's own example, verbatim from README.md:38-43:

var base_url=https://api.example.com
GET {{base_url}}/users

No network access is needed; the URL is fully constructed before the socket opens.

Actual output

README line  : GET {{base_url}}/users
README claims: -> GET https://api.example.com/users

ACTUAL url   : https://api.example.com/https://api.example.com/users
MATCH?       : False

End-to-end through the real REPL binary against a local echo server, with base_url pointing at it:

========== REPL stdout (verbatim) ==========
🌿  Git-API REPL 0.1.0
    Type 'help' for commands, 'quit' to exit.

default> Set base_url = http://127.0.0.1:35107
default> Set token = secret123
default>
→  GET http://127.0.0.1:35107/http://127.0.0.1:35107/users
←  200
{
  "ok": true
}

  (use 'save get-{{base_url}}-users' to save)
default>
💾  Saved to .git-api/requests/tmpl.json
default> curl \
   -X \
   GET \
   http://127.0.0.1:35107/http://127.0.0.1:35107/users
default>
↻  GET http://127.0.0.1:35107/http://127.0.0.1:35107/users
←  200
{
  "ok": true
}
default> Bye.
========== SERVER RECEIVED ==========
[
  {
    "method": "GET",
    "path": "/http://127.0.0.1:35107/users",
    "body": ""
  },
  {
    "method": "GET",
    "path": "/http://127.0.0.1:35107/users",
    "body": ""
  }
]

The server received the path /http://127.0.0.1:35107/users. Note the REPL prints ← 200 and the save/curl/replay round-trip all "work" — the corruption is completely silent, because a doubled path is a perfectly valid URL that a catch-all route happily answers.

Expected output

→  GET http://127.0.0.1:35107/users

matching README.md:42.

The substitution must happen first, so that resolve_url sees either a full http(s):// URL (returned as-is) or a path to join onto the base:

_interpolate first -> 'https://api.example.com/users'  -> returned unchanged

Root cause

git_api/cli.py:139-143 — order of operations inside build_request_parts:

url = resolve_url(parts[1], env)   # line 139: prefixes base_url, leaving "{{base_url}}/users"
...
url = _interpolate(url, env)       # line 143: expands the placeholder *inside* the already-prefixed URL

resolve_url (cli.py:79-89) does not know about {{...}}, so it treats {{base_url}}/users as a relative path and produces base + "/{{base_url}}/users". _interpolate then expands the placeholder to the full base URL, giving base + "/" + base + "/users".

This affects build_request_parts only. replay (cli.py:427) does it in the correct order:

url = resolve_url(_interpolate(req["url"], env), env)   # interpolate, then resolve

so replay resolves {{base_url}}/users correctly while the live path does not. That asymmetry is the clearest confirmation that the ordering in build_request_parts is the bug and not an intentional design choice.

The test suite does not catch this because no test asserts the resolved URL for a {{base_url}}-prefixed input. tests/test_interpolation.py tests _interpolate in isolation, and tests/test_cli.py:377 uses a URL where substitution and joining do not overlap.

Suggested fix

--- a/git_api/cli.py
+++ b/git_api/cli.py
@@ def build_request_parts(parts: list[str], env: dict):
-    url = resolve_url(parts[1], env)
+    # Substitute before joining: resolve_url must see either a full
+    # http(s):// URL or a bare path, never an unexpanded {{base_url}}.
+    url = resolve_url(_interpolate(parts[1], env), env)
     raw_body = parts[2] if len(parts) > 2 else None
     body = _interpolate(raw_body, env) if raw_body is not None else None
     headers = _interpolate_headers(env.get("headers", {}), env)
-    url = _interpolate(url, env)

This makes the live path match the already-correct replay path and makes the README example true.

Worth adding a regression test that asserts the README example resolves to https://api.example.com/users, so the documented example stays covered.

Related

  • Same function as the multi-token body truncation defect (parts[2] only, at cli.py:140).
  • CHANGELOG.md records that {{variable}} substitution was extended to headers in 0.1.0; the ordering fix belongs with it.
Dominant language
Python
Stars
0
Forks
1
Avg merge
37m
Merged PRs (30d)
9

Getting set up

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from yunaremaia/git-api

All issues in yunaremaia/git-api

Similar issues

More Python issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.